Flag SceneNode v1

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
fmx

Flag SceneNode v1

Post by fmx »

This has been a long time coming
dedicated to tecan and everyone else wanting simple flags in their scenes

A very simple and fast scenenode to create a generic flag mesh and apply simple cloth physics to it.

Image

Its probably not the best way of doing it, but it works. And its fast.

Should work on all irrlicht compatible devices, OSX, iPhone, Android, etc

Multiple flag nodes are not a problem, and they can be attached as children to any other node (cars, poles, banners, character joints,... use your imagination).

Each flag has its own gravity vector and arbitrary forces can be applied at will, for example to simulate wind effects (see the demo)


Demo keys:

Q = Pause physics
E = Unpause physics

A = Enable wind
D = Disable wind

W = Wireframe on
S = Wireframe off


version 1
http://www.megaupload.com/?d=Z178YA4Q

Source included with the demo binary.
Released under zlib license.

I cant promise any further updates, but post here if there are any serious problems


Source

CFlagSceneNode.h

Code: Select all

//
// Flag Scene-Node for Irrlicht 1.7.1
//
// v 1.7.1a
// Not very precise, still a little rusty,
// but gets the job done
//
// Created by FMX for the Irrlicht community
// zlib licence
//


#ifndef _C_FLAG_SCENENODE_H_INCLUDED_
#define _C_FLAG_SCENENODE_H_INCLUDED_

#include "irrlicht.h"

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;


class CFlagSceneNode : public scene::ISceneNode
{
	core::aabbox3d<f32> Box;
	video::SMaterial Material;

	array<u16>			Indices;
	array<S3DVertex>	Verts;

	u32					Polys;


	vector3df			Gravity;
	vector3df			NetForce;		// Vector representing Net of External Forces
	
	u32					Iterations;
	f32					SpringStiffness;
	
	struct CC_Spring
	{ public:

		u16				Node_1;				// ID of the Cloth Node this vert refers to
		u16				Node_2;				// ID of the Cloth Node this vert refers to
		float			Length;				// Current Length of Spring
		float			LengthIdle;			// Initial Length of Spring
	};
	u32					NumSprings;

	array<CC_Spring> 	SPRING; 			// storage for all the SPRINGS
	
	array<bool>			VertIsPinned;
	array<vector3df>	VertInitialPos;
	array<vector3df>	VertPosOld;

public:

	virtual void setForce	( const vector3df &newForce ) { NetForce  = newForce; }
	virtual void applyForce	( const vector3df &addForce ) { NetForce += addForce; }
	virtual void setGravity	( const vector3df &gravity )  { Gravity = gravity; }

	~CFlagSceneNode()
	{
		Indices.clear();
		Verts.clear();
		SPRING.clear();
		VertIsPinned.clear();
		VertInitialPos.clear();
		VertPosOld.clear();
	}

	CFlagSceneNode
		(  
			const f32 &Height,	// height of flag
			const f32 &Length,	// length of flag
			const int &HeightTesselation, // tessaltion along height
			const int &LengthTesselation, // tessaltion along length

			SMaterial Mat,				// initial material properties

			const vector3df &gravity,	// initial gravity
			const u32		&iterations, 
			const f32		&spring_stiffness,

			scene::ISceneNode* parent, 
			scene::ISceneManager* mgr, s32 id
		)
		: 
			scene::ISceneNode(parent, mgr, id),
			Iterations		( iterations ),
			SpringStiffness	( spring_stiffness ),
			Gravity			( gravity ),
			Material		( Mat ) 
	{
		NetForce = vector3df(0);

	// generate Flag mesh
		Verts.clear();

		VertIsPinned.clear();
		VertInitialPos.clear();
		VertPosOld.clear();

		Indices.clear();
		Polys = 0;

		NumSprings = 0; 
  		SPRING.clear(); 

		Box.reset( vector3df(0) );

		f32 xD = Length / f32(LengthTesselation);
		f32 yD = Height / f32(HeightTesselation);

		f32 xUV = 1.f / f32(LengthTesselation);
		f32 yUV = 1.f / f32(HeightTesselation);

		for( int y = 0;  y < HeightTesselation+1;  y++ )
		{
			for( int x = 0;  x < LengthTesselation+1;  x++ )
			{
				S3DVertex v;
					v.Color	  = SColor( 255,  255, 255, 255 );
					v.Pos	  = vector3df( xD*x, yD*y, 0 );
					v.TCoords = vector2df( xUV*x, 1.f-yUV*y );
				Verts.push_back( v );

				if( x == 0 ){ VertIsPinned.push_back( true  ); }
				else		{ VertIsPinned.push_back( false ); }

				VertInitialPos.push_back( v.Pos );
				VertPosOld.push_back	( v.Pos );
				
				Box.addInternalPoint(v.Pos);
   			}
		}
		Box.repair();


		for( int y = 0;  y < HeightTesselation;  y++ )
		{
			for( int x = 0;  x < LengthTesselation;  x++ )
			{
				int iV[4];
					iV[0] = (y*(LengthTesselation+1))+x;
					iV[1] = iV[0]+1;
					iV[2] = iV[0]+LengthTesselation+1;
					iV[3] = iV[1]+LengthTesselation+1;

				if( iV[0] < Verts.size() )
				{
					Indices.push_back( iV[0] );
					Indices.push_back( iV[2] );
					Indices.push_back( iV[1] );

					Indices.push_back( iV[1] );
					Indices.push_back( iV[2] );
					Indices.push_back( iV[3] );

					Polys += 2;

				// Springs
					CC_Spring 	temp_SPRING;
						temp_SPRING.Node_1 = iV[0];
						temp_SPRING.Node_2 = iV[1];
					SPRING.push_back( temp_SPRING );
						temp_SPRING.Node_1 = iV[0];
						temp_SPRING.Node_2 = iV[2];
					SPRING.push_back( temp_SPRING );

					if( x == LengthTesselation - 1 )
					{
						temp_SPRING.Node_1 = iV[1];
						temp_SPRING.Node_2 = iV[3];
					SPRING.push_back( temp_SPRING );
					}

					if( y == HeightTesselation - 1 )
					{
						temp_SPRING.Node_1 = iV[2];
						temp_SPRING.Node_2 = iV[3];
					SPRING.push_back( temp_SPRING );
					}
				}
			}
		}

		NumSprings = SPRING.size(); 
	}

	virtual void OnRegisterSceneNode()
	{
		if (IsVisible) { SceneManager->registerNodeForRendering(this); }

		ISceneNode::OnRegisterSceneNode();
	}

	virtual void render()
	{
		if( !Verts.size() ) return;

		// Render
		video::IVideoDriver* driver = SceneManager->getVideoDriver();

		driver->setMaterial(Material);
		driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
		driver->drawVertexPrimitiveList(&Verts[0], Verts.size(), &Indices[0], Polys, video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
	}

	virtual const core::aabbox3d<f32>& getBoundingBox() const
	{
		return Box;
	}

	virtual u32 getMaterialCount() const
	{
		return 1;
	}

	virtual video::SMaterial& getMaterial(u32 i)
	{
		return Material;
	}	


		
	// UPDATE
	void UpdatePhysics( const f32 &deltaTime )
	{
		u32 i, n, s;  

		matrix4 Mi = getAbsoluteTransformation();
		Mi.makeInverse();

	// rotate forces by nodes orientation
		vector3df Force = NetForce + Gravity;
		Mi.rotateVect(Force);
		Force *= getScale();

	// Apply Forces to every NODE, and update its Position
  		for( n = 0;  n < Verts.size();  n ++ )
  		{
			vector3df position = Verts[ n ].Pos; 
    		if( !VertIsPinned[ n ] )	{ Verts[ n ].Pos += (position - VertPosOld[ n ]) * 0.99f + Force * deltaTime * deltaTime;  }
    		else						{ Verts[ n ].Pos = VertInitialPos[ n ]; }
    		VertPosOld[ n ] = position;
  		}
		NetForce = vector3df(0);

	// Adujust the NODEs depending on how the SPRINGs have changed
  		for( i = 0;  i < Iterations;  i ++ )
  		{
    		for( s = 0;  s < NumSprings;  s ++ )
    		{
				u16 vert_1 = SPRING[ s ].Node_1;
				u16 vert_2 = SPRING[ s ].Node_2;

      			vector3df	d = Verts[ vert_2 ].Pos - Verts[ vert_1 ].Pos;
				SPRING[ s ].Length = d.getLength();
				if( SPRING[ s ].Length < 0 ){ SPRING[ s ].Length = 0 - SPRING[ s ].Length; }

      			if( SPRING[ s ].Length > 0 )
      			{			   		
					f32 vert_mod = (SPRING[ s ].Length - SPRING[ s ].LengthIdle ) / SPRING[ s ].Length;

		   			if( !VertIsPinned[ vert_1 ] )
		   			{
		   				if( !VertIsPinned[ vert_2 ] )
						{
							d *= vert_mod / SpringStiffness;
           					Verts[ vert_1 ].Pos += d;
           					Verts[ vert_2 ].Pos -= d;
						}
						else
						{ Verts[ vert_1 ].Pos += d * vert_mod; }
					}
					else if( !VertIsPinned[ vert_2 ] )
					{ Verts[ vert_2 ].Pos -= d * vert_mod; }

           		}
    		}
  		}

	}


};



#endif  // _C_FLAG_SCENENODE_H_INCLUDED_


An example to use it

Code: Select all


#include "CFlagSceneNode.h"


...


// create the node
SMaterial Mat;
Mat.Lighting		= false;
Mat.BackfaceCulling = false;

CFlagSceneNode *irrFlagNode = new CFlagSceneNode
		( 
			10, 20,		// flag size
			10, 20,		// flag tesselation
			Mat,			// material
			vector3df(0,-10,0),	// gravity

			2,		// iterations (only change these two to tweak the simulation)
			1.99f,	// spring-stiffness 

			smgr->getRootSceneNode(), smgr, -1	// parent, irrlicht scene manager, ID
		);


...


// in your main loop 

irrFlagNode->applyForce( vector3df( 10) ); // wind 
irrFlagNode->UpdatePhysics( 0.02f ); // dt 


...


//terminate
irrFlagNode->drop();

Last edited by fmx on Mon Sep 20, 2010 6:04 pm, edited 1 time in total.
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Post by CuteAlien »

I just tested it here and it works great. Also very easy to use, I like it :-)
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
pilesofspam
Posts: 62
Joined: Mon May 11, 2009 4:31 am

Post by pilesofspam »

This is great. I'd like to try it, but megaupload says 'The file you are trying to access is temporarily unavailable'
Eigen
Competition winner
Posts: 375
Joined: Fri Jan 27, 2006 2:01 pm
Location: Estonia
Contact:

Post by Eigen »

fmx

Post by fmx »

thanks Eigen
I also pasted the code in the first post
jorgerosa
Competition winner
Posts: 117
Joined: Wed Jun 30, 2010 8:44 am
Location: Portugal
Contact:

Post by jorgerosa »

Just what i needed to add to my project! THANKYOU for share! :D
TheMrCerebro
Competition winner
Posts: 80
Joined: Tue Jun 29, 2010 10:06 pm
Location: Valencia, Spain

Post by TheMrCerebro »

Hey!! I love the flag effect.
THANKS!!
:wink:
Follow me on twitter: @themrcerebro
netpipe
Posts: 670
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada
Contact:

Post by netpipe »

not sure why but the flag does not work the same under opengl + linux.



thanks again fmx , this was worth the wait :)

Image
Live long and phosphor!
-- https://github.com/netpipe/Luna Game Engine Status 95%
netpipe
Posts: 670
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada
Contact:

Post by netpipe »

SPRING[ s ].LengthIdle is not set anywhere is this normal ? its like its not springing back at all.
Live long and phosphor!
-- https://github.com/netpipe/Luna Game Engine Status 95%
Josh1billion
Posts: 125
Joined: Thu Dec 11, 2008 9:50 pm
Location: Wisconsin
Contact:

Post by Josh1billion »

Thanks for sharing this. I'm going to be implementing it into a project soon. :)
www.JoshForde.com

Latest release: Super Orbulite World.
In development: Season of Dreams and others
Brainsaw
Posts: 1183
Joined: Wed Jan 07, 2004 12:57 pm
Location: Bavaria

Post by Brainsaw »

This is great. I think I'll add it to my repository.
Dustbin::Games on the web: https://www.dustbin-online.de/

Dustbin::Games on facebook: https://www.facebook.com/dustbingames/
Dustbin::Games on twitter: https://twitter.com/dustbingames
SSJ17Vegeta
Posts: 19
Joined: Thu Nov 13, 2008 9:11 am

Post by SSJ17Vegeta »

Hi and first of all thanks for your work on your flag scene node.

Alas, I confirm it's not working on OpenGL.

The flag behaves strangely, it "waves" once, then waves on the other side, then slowly (over a few seconds) stops waving to finally disappear : I used the same camera positions, node scales as in your example , simply wrapping it in a CGUIViewPort to include it in a GUI page.

Am I doing something wrong ?

Construction (GUI element constructor) :

Code: Select all

irr::core::vector3df	GravityVec(0, -10.f, 0);
	doWind 			= true;
	WindWave 		= 0;
	showWire		= false;
	pausePhysics	= false;
	textureChanged	= false;

	ISceneManager* smgr = GameManager::Instance()->getSceneManager()->createNewSceneManager();
	setSceneManager(smgr);
	smgr->addCameraSceneNode(0, core::vector3df(0,125,-250), core::vector3df(0,0,0));

	this->internalNode = new CFlagSceneNode
		(
			10, 20,		// flag dimensions (change to whatever size required, or scale the node later)
			10, 20,		// flag tesselation (good default value)

			GravityVec,	// gravity (depends on the scale of the scene and the tesselation)

			2,			// iterations (change at your own peril)
			1.99f,		// spring-stiffness (change at your own peril)
			getSceneManager()->getRootSceneNode(), getSceneManager(), 1	// parent, irrlicht scene manager, ID
		);
internalNode->setScale( vector3df(10) );
( Texture is passed somewhere else ).

Rendering (GUI Element / method "draw") :

Code: Select all

if( doWind )
	{
		WindWave += 0.01f;
		if( WindWave > 1 ) { WindWave = 0; }

		vector3df Wind = vector3df(
						 (sin( WindWave * 3 ) + 0.5f) * 5,
						 (sin( WindWave ) * 0.5f) * 40,
						 (cos( WindWave * 3)) * 5
						 );
		internalNode->applyForce( Wind ); // Apply the Force
	}
	if( !pausePhysics ) // update irrFlag Physics (must provide an accurate dt, to pause it simply dont update it)
	{
		internalNode->UpdatePhysics( 0.02f );
	}
Thanks in advance for taking the time to answer ^^[/code]
netpipe
Posts: 670
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada
Contact:

Post by netpipe »

bump! , any news on a OpenGL fix ?
Live long and phosphor!
-- https://github.com/netpipe/Luna Game Engine Status 95%
fmx

Post by fmx »

It works with GL on my development system (winXP) seems to be a problem on other OS

The behaviour is wonky because the physics requires an accurate timer and I didn't implement one in the test demo.
It seems you expect a fully contained independent SceneNode with an internal timer and not just a bare setup.

I will update this snippet soon, although not likely before the new year, I'm very busy right now
netpipe
Posts: 670
Joined: Fri Jun 06, 2008 12:50 pm
Location: Edmonton, Alberta, Canada
Contact:

Re: Flag SceneNode v1

Post by netpipe »

got it working, it was the spring idle and delta time... sweetnessss
Live long and phosphor!
-- https://github.com/netpipe/Luna Game Engine Status 95%
Post Reply