(C++) Rotating 3d sprite (RotatingBillboard)

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Warchief
Posts: 204
Joined: Tue Nov 22, 2005 10:58 am

(C++) Rotating 3d sprite (RotatingBillboard)

Post by Warchief »

People claims for a rotating billboard; here it goes.

Effect:
Image
Image
Image

You can download a demo (source code, image, exe and dll) here:
http://www.megaupload.com/?d=2L3T0VOY

Code: Select all

// CRotatingBillboardNode.h file
// gaz<@NOSPAM>bitplane.net - no rights reserved

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

class TRotatingBillboardSceneNode : public ISceneNode
 {
private:

	core::dimension2d<f32> Size;
	core::aabbox3d<f32> BBox;
	video::SMaterial Material;

    s16 rotatecount, framecount, currentframe;
    s16 lastimagex;
    core::dimension2d<s32> imagesize;
    core::dimension2d<s32> texturesize;

	video::S3DVertex vertices[4];
	u16 indices[6];
	
public:

// constructor
    TRotatingBillboardSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id, 
        const core::vector3df& position = core::vector3df(0,0,0), 
        const core::dimension2d<f32>& size = dimension2d<f32>(10,10)) : ISceneNode(parent, mgr, id)
    {  

    	setSize(size);
    
    	AutomaticCullingEnabled = false;
    
    	indices[0] = 0;
    	indices[1] = 2;
    	indices[2] = 1;
    	indices[3] = 0;
    	indices[4] = 3;
    	indices[5] = 2;
    
    	vertices[0].TCoords.set(0.0f, 0.0f);
    	vertices[0].Color = 0xffffffff;
    
    	vertices[1].TCoords.set(0.0f, 1.0f);
    	vertices[1].Color = 0xffffffff;
    
    	vertices[2].TCoords.set(1.0f, 1.0f);
    	vertices[2].Color = 0xffffffff;
    
    	vertices[3].TCoords.set(1.0f, 0.0f);
    	vertices[3].Color = 0xffffffff;
            
    }

    ~TRotatingBillboardSceneNode()
    { 
    }
    
    void TRotatingBillboardSceneNode::OnPreRender()
    {
    	if (IsVisible)
    	{
    		SceneManager->registerNodeForRendering(this);
    		ISceneNode::OnPreRender();
    	}
    }
     
    void setSprites(s16 rotationframes, s16 animframes, core::dimension2d<s32> spritedimensions,  s16 currentanimframe =0)
    {
        rotatecount = rotationframes;
        framecount  = animframes;
        imagesize  = spritedimensions;
        currentframe = currentanimframe;
        SMaterial t = getMaterial(0);
        ITexture *tx = t.Textures[0];
        texturesize = tx->getSize();
    }
    void setFrame(s16 framenumber) 
    {
        currentframe = framenumber;
    }
    
    vector3df getTargetAngle(vector3df v, vector3df r) 
    { 
       //v -position 
       //r -target 
    
       vector3df angle; 
       float x,y,z; 
       x = r.X - v.X; 
       y = r.Y - v.Y; 
       z = r.Z - v.Z; 
        
       //angle in X-Z plane 
       angle.Y = atan2 (x, z); 
       angle.Y *= (180 / PI); //converting from rad to degrees 
       //angle.Y-=90;
       //just making sure angle is somewhere between 0-360 degrees 
       if(angle.Y < 0) angle.Y += 360; 
       if(angle.Y >= 360) angle.Y -= 360; 
        
       //angle in Y-Z plane while Z and X axes are already rotated around Y 
       float z1 = sqrt(x*x + z*z); 
        
       angle.X = atan2 (z1, y); 
       angle.X *= (180 / PI); //converting from rad to degrees 
       //angle.X -= 90; 
        
       //just making sure angle is somewhere between 0-360 degrees 
       if(angle.X < 0) angle.X += 360; 
       if(angle.X >= 360) angle.X -= 360; 
    
       return angle; 
    } 

    //! render
    void TRotatingBillboardSceneNode::render()
    {
    	video::IVideoDriver* driver = SceneManager->getVideoDriver();
    	ICameraSceneNode* camera = SceneManager->getActiveCamera();
    
    	if (!camera || !driver)
    		return;
    
    	// make billboard look to Camera
    
    	core::vector3df pos = getAbsolutePosition();
    
    	core::vector3df campos = camera->getAbsolutePosition();
    	core::vector3df target = camera->getTarget();
    	core::vector3df up = camera->getUpVector();
    	core::vector3df view = target - campos;
    	view.normalize();
    
    	core::vector3df horizontal = up.crossProduct(view);
    	horizontal.normalize();
    
    	core::vector3df vertical = horizontal.crossProduct(view);
    	vertical.normalize();
    
    	horizontal *= 0.5f * Size.Width;
    	vertical *= 0.5f * Size.Height;	
    
    	vertices[0].Pos = pos + horizontal + vertical;
    	vertices[1].Pos = pos + horizontal - vertical;
    	vertices[2].Pos = pos - horizontal - vertical;
    	vertices[3].Pos = pos - horizontal + vertical;

    	view *= -1.0f;
    
    	for (s32 i=0; i<4; ++i)
    		vertices[i].Normal = view;

        // get rotation sprite number
        vector3df selfrot=getRotation();
        vector3df targetrot;
        targetrot=getTargetAngle(pos,campos);
        f32 rotadd=targetrot.Y-selfrot.Y;
        
        s16 imagex = rotatecount-1 - int(rotadd / (360.0/rotatecount));
        
        if (lastimagex != imagex) {
            // get the texture position
            f32 x1     = (imagex * imagesize.Width) ;
                x1    /= texturesize.Width;
            f32 y1     = (currentframe * imagesize.Height);
                y1    /= texturesize.Height;
            f32 x2     = ((imagex+1) * imagesize.Width); 
                x2    /= texturesize.Width;
            f32 y2     = ((currentframe+1) * imagesize.Height);
                y2    /= texturesize.Height;
            
            // set texture
            
        	vertices[0].TCoords.set(x1, y2);
        	vertices[1].TCoords.set(x1, y1);
        	vertices[2].TCoords.set(x2, y1);
        	vertices[3].TCoords.set(x2, y2);
        }
        lastimagex = imagex;
    	// draw
    
    	core::matrix4 mat;
        driver->setTransform(video::ETS_WORLD, mat);
    	driver->setMaterial(Material);
    
    	driver->drawIndexedTriangleList(vertices, 4, indices, 2);
    }
    
    
    //! returns the axis aligned bounding box of this node
    const core::aabbox3d<f32>& TRotatingBillboardSceneNode::getBoundingBox() const
    {
    	return BBox;
    }
    
    
    //! sets the size of the billboard
    void TRotatingBillboardSceneNode::setSize(const core::dimension2d<f32>& size)
    {
    	Size = size;
    }
    
    
    video::SMaterial& TRotatingBillboardSceneNode::getMaterial(s32 i)
    {
    	return Material;
    }
    
    
    //! returns amount of materials used by this scene node.
    s32 TRotatingBillboardSceneNode::getMaterialCount()
    {
    	return 1;
    }
    
    
    //! gets the size of the billboard
    const core::dimension2d<f32>& TRotatingBillboardSceneNode::getSize()
    {
    	return Size;
    }
          
 };  
A main example (1 auto rotating sprite, and lots of facing rotating sprites):

Code: Select all

#include <stdio.h>
#include <wchar.h>
#include <irrlicht.h>

using namespace irr;

#include "CRotatingBillboardNode.h"
 
#pragma comment(lib, "Irrlicht.lib")


#define MAXSPRITES 50
scene::ISceneNode* node = 0;
TRotatingBillboardSceneNode* manysprites[MAXSPRITES];
IrrlichtDevice* device = 0;


int main()
{

	device = createDevice(video::EDT_OPENGL, core::dimension2d<s32>(800, 600),
		16, false, false, false);

	video::IVideoDriver* driver = device->getVideoDriver();
	scene::ISceneManager* smgr = device->getSceneManager();



    TRotatingBillboardSceneNode* node = new TRotatingBillboardSceneNode( smgr->getRootSceneNode(), smgr, 666); 

    node->setMaterialFlag(EMF_LIGHTING, false);
    node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); //makes it transparent
    ITexture * txture = driver->getTexture("fairy.bmp");
    driver->makeColorKeyTexture (txture,position2d<s32>(0,0));
    node->setMaterialTexture( 0, txture );
    node->setSprites(16,1,core::dimension2d<s32>(64,103),0);
    
    // we'll have a bank of sprites
    for (int n=0;n<MAXSPRITES;n++) {
       manysprites[n] = new TRotatingBillboardSceneNode( smgr->getRootSceneNode(), smgr, 667+n); 
       manysprites[n]->setMaterialTexture( 0, txture );
       manysprites[n]->setMaterialFlag(EMF_LIGHTING, false);
       manysprites[n]->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); //makes it transparent
       manysprites[n]->setSprites(16,1,core::dimension2d<s32>(64,103),0);
       manysprites[n]->setPosition(vector3df((n - n%(MAXSPRITES/10) )*5+10,n %(MAXSPRITES/10)*-30,0));
    }
	

	ICameraSceneNode *camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 100.0f);
	device->getCursorControl()->setVisible(false);
	int lastFPS = -1;

    f32 rot=0.0;
    
	while(device->run())
	{
		driver->beginScene(true, true, video::SColor(255,113,113,133));
		smgr->drawAll();
		driver->endScene();
		rot +=0.1;
        node->setRotation(vector3df(0,rot,0));
		int fps = driver->getFPS();
        
		if (lastFPS != fps)
		{
			wchar_t tmp[1024];
			swprintf(tmp, 1024, L"Rotating Billboards! [%s] fps:%d", 
				driver->getName(), fps);

			device->setWindowCaption(tmp);
			lastFPS = fps;

		}
		
	}

	/*
	In the end, delete the Irrlicht device.
	*/
	device->drop();
	
	return 0;
}
Disclaimer:
Note the code is not mine; i have not used, compiled or revised it. I dont know if it works with all driver types. Im just posting what i found somewhere i havent been able to find again.
The guy gaz<@NOSPAM>bitplane.net seems to be the author.
bitplane
Admin
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England
Contact:

Post by bitplane »

thats me :D

x axis is rotation, y axis is animation. it doesnt animate by its self, you need to set the frame manually

for a more permanent link, i uploaded it here a while back-
http://gdlib.net/index.php?name=Downloa ... ails&id=15
Submit bugs/patches to the tracker!
Need help right now? Visit the chat room
Warchief
Posts: 204
Joined: Tue Nov 22, 2005 10:58 am

Post by Warchief »

So here he is.
bitplane
Admin
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England
Contact:

Post by bitplane »

update: particle systems and a texture generator

Image

download:
http://gdlib.net/index.php?name=Downloa ... ails&id=84

i'll probably start a thread in projects when the texture generator has a gui and stuff

edit: the rocks demo isnt included, but i did make a video of it...
http://video.google.com/videoplay?docid ... 7570381047
Submit bugs/patches to the tracker!
Need help right now? Visit the chat room
IPv6
Posts: 188
Joined: Tue Aug 08, 2006 11:58 am

One more question

Post by IPv6 »

Hello!
May i ask one question: is this class capable of "true" billboard rotation in the plane perpendicular to camera`s field of view? sources are placed at megaupload and i can not download them because this server expose very limited bandwidth to our counrty (russia) and rejects attempts to download sources and demo for futher research.

as i see from code angle is used to pick right frame of the animation only :( is it possible to rotate billboard as "3d rectangle" in XY plane (camera space)? for example by modifing mat in this piece of code

Code: Select all

core::matrix4 mat;
driver->setTransform(video::ETS_WORLD, mat);
driver->setMaterial(Material);
driver->drawIndexedTriangleList(vertices, 4, indices, 2);
Warchief
Posts: 204
Joined: Tue Nov 22, 2005 10:58 am

Post by Warchief »

You can grab the code at the link provided by bitplane:
http://gdlib.net/index.php?name=Downloa ... ails&id=15


I dont know about the question, let's wait for bitplane to reply that.
IPv6
Posts: 188
Joined: Tue Aug 08, 2006 11:58 am

Post by IPv6 »

thanks for the link!!!

i checked examples and found that this is not the thing i`m searching. angle is used to get fairy animation frame. it looks like fairy is rotationg over Y axis (in X-Z plane). But i looking for method for rotation billboard over Z axis (in X-Y plane)

Code: Select all

Y
|   Z
|   /
|  /
| /
O----------X
is it possible?
IPv6
Posts: 188
Joined: Tue Aug 08, 2006 11:58 am

Post by IPv6 »

Heh, just found it by myself: it is sufficient to modify mat in render() before applying it to world transformation by initializing with AbsoluteTransformation and removing pos dependency from vertices
Post Reply