modification for particles system

Discuss about anything related to the Irrlicht Engine, or read announcements about any significant features or usage changes.
calimero
Posts: 45
Joined: Sun Aug 08, 2004 4:31 pm
Location: Nice, France

modification for particles system

Post by calimero »

here is my proposal (as small as possible) to change the behaviour of the particules : as soon as they are emitted they are computed in world coordinates and not in local system node coordinates. The effect is quite pretty with a moving particles system node (like circle animator).

I know other people have made proposal to do the same but I think this method is the simplest and doesn't alter performances.

here is an example of the changed system particle rotating around a character (with circleanimator)
Image

the only changes are in the CParticleSystemNode.cpp file

there is 2 places to updates (one to declare an identity matrix for rendering particle instead of local matrix of the system node and the other to update particle position in world coordinates after creation):

one side effect is that you lost the ability to scale the whole system with the methods setScale() but I think that a minor drawback.

* in the render method

Code: Select all

// render all 
	core::matrix4 matid;
	driver->setTransform(video::ETS_WORLD, matid);
instead of the old setTransform line

and in the doParticleSystem(u32 time) method
replace the // run emitter paragraph with this one :

Code: Select all

// run emitter

	if (Emitter)

	{

		SParticle* array = 0;

		s32 newParticles = Emitter->emitt(now, timediff, array);


		core::vector3df absolutepos = getAbsolutePosition();
		if (newParticles && array)

			for (s32 i=0; i<newParticles && Particles.size() < 16250; ++i)
			   {
			   array[i].pos = absolutepos;
			   Particles.push_back(array[i]);
			   }	

	}
Electron
Posts: 874
Joined: Sun Mar 14, 2004 12:05 am
Location: Massachusetts USA

Post by Electron »

sweet pics, and I like your idea.
You do a lot of programming? Really? I try to get some in, but the debugging keeps me pretty busy.

Crucible of Stars
Guest

Post by Guest »

nice pic! :D
niko
Site Admin
Posts: 1759
Joined: Fri Aug 22, 2003 4:44 am
Location: Vienna, Austria
Contact:

Post by niko »

Yep, I think your modification makes sence. Maybe it would be a good idea to add an option for making it behave the old way with relative coordinates, but I'm not sure. :)
calimero
Posts: 45
Joined: Sun Aug 08, 2004 4:31 pm
Location: Nice, France

Post by calimero »

niko,

we discuss that on irrlivhtnx forum with jox. Probably the good way is to put a bool like particleGlobal set to false by default in the constructor and add a method like setparticleGlobal.

Another setting may be to preserve the "local" emitter vector because with my patch I think all parameters become global

so we have have
3 settings :

* legacy mode (like now)
* full global (like my current patch)
* emitter vector local but all others effects global.
Electron
Posts: 874
Joined: Sun Mar 14, 2004 12:05 am
Location: Massachusetts USA

Post by Electron »

Yeah, there could be cases when one would want to keep the particles together
You do a lot of programming? Really? I try to get some in, but the debugging keeps me pretty busy.

Crucible of Stars
calimero
Posts: 45
Joined: Sun Aug 08, 2004 4:31 pm
Location: Nice, France

new proposal

Post by calimero »

ok, I made some test with a local emitter vector with global particles.
I'm not sure how to interface it properly.

but I suppose I have 2 booleans :

bool isParticleGlobal;
bool isEmitterVecGlobal;


if isParticleGlobal=false we have legacy mode (the other bool doesn't matter)

if isParticleGlobal=true :
the particle are computed in world coordinates.
if isEmitterVecGlobal=true then the emitter vector is expressed in world coordinates elsewhere it's expressed in particleSystemNode coordinates.


ok then the code (only the cpp file):

in the render method :

Code: Select all

// render all 
	if (isParticleGlobal)
	   {
	   core::matrix4 matid;
	   driver->setTransform(video::ETS_WORLD, matid);
	   }
	else driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); 


in the doParticleSystem(u32 time) method :

Code: Select all

// run emitter

	if (Emitter)

	{

		SParticle* array = 0;

		s32 newParticles = Emitter->emitt(now, timediff, array);

		core::vector3df absolutepos = getAbsolutePosition();
		if (newParticles && array)

			for (s32 i=0; i<newParticles && Particles.size() < 16250; ++i)
			   {
			   if (isParticleGlobal && (!isEmitterVecGlobal)) getAbsoluteTransformation().rotateVect(array[i].startVector);
               if (isParticleGlobal) array[i].pos = absolutepos;
			   Particles.push_back(array[i]);
			   }	

	}
hope it works well.

I need help to implement a proper interface to set the booleans (in the constructor or via additional methods ?)
Guest

Post by Guest »

I opt for both, constructor and methods
jox
Bug Slayer
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Post by jox »

Well, how about 3 modes as an enum? Than it's one parameter.

scene::EPSM_LOCAL
scene::EPSM_GLOBAL
scene::EPSM_GLOBAL_EMITTER_LOCAL

(EPSM = enum particle system mode)

Anybody has proposals for better names?

What should be default? Normaly it would be scene::EPSM_LOCAL.
jox
Bug Slayer
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Post by jox »

Ok i've implemented it!

I'm not sure about the EPSM_GLOBAL_EMITTER_LOCAL

I tested it with the special effects example by adding the flyCircleAnimator of the billboard to the particle system.

With EPSM_LOCAL it looks like this:
(original irrlicht behaviour)

Image

With EPSM_GLOBAL it's looks this cool :)

Image

But with with EPSM_GLOBAL_EMITTER_LOCAL the particles are a bit outta space...

Image

Is it normal?
position and rotation are both (0,0,0)

So is this code correct?

Code: Select all

					getAbsoluteTransformation().rotateVect(array[i].startVector);
calimero
Posts: 45
Joined: Sun Aug 08, 2004 4:31 pm
Location: Nice, France

Post by calimero »

hello jox thanks a lot for doing the integration job !!!
for the last setting I'm surprised normally I've seen that circle animator move the object but doesn't rotate it so normally you may have had the same result as full global. maybe I misunderstood what the startvector is for Sparticle ? I have made just one test on my side and it seemed to work well but I will look at this.

jox can you try to set a particle emitter with a null emitter vector and then test it with the last 2 modes, normally you wouldn't see any differences.

p.s. i'm not sure but it may be a problem with the bouding box of the node, I don't care of it but with the modification it may be no correctly updated anymore. I say that because I experiment some strange clipping sometimes.
jox
Bug Slayer
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Post by jox »

Sorry, forget screenshot #3 and the EPSM_GLOBAL_EMITTER_LOCAL thing. I did a mistake.

But another thing: how can I test the difference between EPSM_GLOBAL_EMITTER_LOCAL and EPSM_GLOBAL. I always get the same results. Any particle setup code example(s)?
calimero
Posts: 45
Joined: Sun Aug 08, 2004 4:31 pm
Location: Nice, France

Post by calimero »

hello jox,

you will see a difference if the orientation of the particle system node is different from world orientation.
here is an example (direct copy paste from my code):

Code: Select all

scene::IParticleSystemSceneNode* ps = 0;
	ps = smgr->addParticleSystemSceneNode(false,(ISceneNode*)((*(++listCar.begin()))->getNode())); // you can attach it directly to rootscenenode
	//ps->setPosition(core::vector3df(50,50,50));
	//ps->setScale(core::vector3df(2,2,2));


ps->setParticleSize(core::dimension2d<f32>(20.0f, 20.0f));

	scene::IParticleEmitter* em = ps->createBoxEmitter(
		core::aabbox3d<f32>(-7,0,-7,7,1,7), 
		core::vector3df(0.1f,0.01f,0.0f),   // here is the emitter vector
		50,100, 
		video::SColor(0,255,255,255), video::SColor(0,255,255,255),
		500,1000,5);
	ps->setParticleSize(core::dimension2d<f32>(10,10));
	ps->setEmitter(em);
	em->drop();

	scene::IParticleAffector* paf = ps->createFadeOutParticleAffector(video::SColor(0,0,0,0),500); 
	ps->addAffector(paf);
	paf->drop();
	vector3df v1 = vector3df(0.0f,0.1f,0.0f);
    vector3df v2 = -v1;
	paf = ps->createGravityAffector(v1);
    ps->addAffector(paf);
	paf->drop();
  
	ps->setMaterialFlag(video::EMF_LIGHTING, false);
	ps->setMaterialTexture(0, driver->getTexture("C:/irrlicht/media/particle.bmp"));
	ps->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
	//anim = smgr->createFlyCircleAnimator(vector3df(0,20,0),30,0.003f);
    anim = smgr->createRotationAnimator(vector3df(0,0.8f,0));
    ps->addAnimator(anim);
    anim->drop();
with code like that you'll see the difference.

last thing : I use a little optimization in my systemparticle code (in the render method) :

Code: Select all

core::vector3df v0 = horizontal + vertical;
	core::vector3df v1 = horizontal - vertical;
	

	// reallocate arrays, if they are too small

	reallocateBuffers();



	// create particle vertex data
	s32 idx = 0;
	for (u32 i=0; i<Particles.size(); ++i)

	{

		SParticle& particle = Particles[i];

		Vertices[idx].Pos = particle.pos + v0;

		Vertices[idx].Color = particle.color;

		Vertices[idx].Normal = view;
		idx++;


		Vertices[idx].Pos = particle.pos + v1;

		Vertices[idx].Color = particle.color;

		Vertices[idx].Normal = view;
		idx++;


		Vertices[idx].Pos = particle.pos - v0;

		Vertices[idx].Color = particle.color;

		Vertices[idx].Normal = view;
		idx++;


		Vertices[idx].Pos = particle.pos - v1;

		Vertices[idx].Color = particle.color;

		Vertices[idx].Normal = view;
		idx++;
	}
the benefits are the suppression of 4 vector additions per particle(for calculation of particles position and the suppression of multiplications to calculate indices of particles.

I'm using theses little optimizations for a long time now and haven't exprimented any problems
jox
Bug Slayer
Posts: 726
Joined: Thu Apr 22, 2004 6:55 pm
Location: Germany

Post by jox »

Thanx calimero! I added it now to the IrrlichtNX CVS!

http://www.irrlichtnx.mmdevel.de/phpBB2 ... .php?p=482

I also included your optimization.
calimero
Posts: 45
Joined: Sun Aug 08, 2004 4:31 pm
Location: Nice, France

patch of the patch

Post by calimero »

sorry jox for discovering the problem just now but the modes "global" don't respect the box emitters.

we have to make this slight modification :

Code: Select all

void CParticleSystemSceneNode::doParticleSystem(u32 time)
{
	u32 now = os::Timer::getTime();
	u32 timediff = now - lastEmitTime;
	lastEmitTime = now;

	// run emitter
	// jox: changed for particle system global patch by calimero
	if (Emitter)
	{
		SParticle* array = 0;
		s32 newParticles = Emitter->emitt(now, timediff, array);

		if (newParticles && array) {
			core::matrix4 absolutetransf = getAbsoluteTransformation();
			for (s32 i=0; i<newParticles && Particles.size() < 16250; ++i) {
				if (Mode == EPSM_GLOBAL_EMITTER_LOCAL)
					absolutetransf.rotateVect(array[i].startVector);
				if (Mode != EPSM_LOCAL)
					absolutetransf.transformVect(array[i].pos);  // here is the important line to change
				Particles.push_back(array[i]);
			}   
		}

	}
i also implement a new mode (EPSM_GLOBAL_AXIALBILLBOARD) with axial billboard instead of standard billboard (nice for making rain) but take it like a beta version and as usual it lacks for interface to set the width and the orientation vector of the axialbillboard

if you are interested here is the code :

Code: Select all

//! render
// jox: changed for particle system global patch by calimero
void CParticleSystemSceneNode::render()
{
	video::IVideoDriver* driver = SceneManager->getVideoDriver();
	ICameraSceneNode* camera = SceneManager->getActiveCamera();

	if (!camera || !driver)
		return;

    // reallocate arrays, if they are too small
	reallocateBuffers();

	// calculate vectors for letting particles look to camera
	core::vector3df pos = getAbsolutePosition();
	core::vector3df campos = camera->getAbsolutePosition();
	
	if (Mode==EPSM_GLOBAL_AXIALBILLBOARD)
	   {
	   Material.BackfaceCulling = false; 
                 
                   // as you can see here I set directly the values
	   f32 width = 0.3f; 
	   core::vector3df orientation = core::vector3df(0,-15,0);

	   s32 idx = 0;
	   for (u32 i=0; i<Particles.size(); ++i)
		   {
		SParticle& particle = Particles[i];
	   pos = particle.pos;
       core::vector3df perp = orientation.crossProduct(campos - pos);
    
       perp.setLength(width);
    // update normals of the node
	core::vector3df norm = (orientation).crossProduct(perp);
	norm.normalize();
    
	Vertices[idx].Pos = pos + perp;
	Vertices[idx].Color = particle.color;
	Vertices[idx].Normal = norm;
	idx++;
	Vertices[idx].Pos = pos + orientation + perp;
	Vertices[idx].Color = particle.color;
	Vertices[idx].Normal = norm;
	idx++;
	Vertices[idx].Pos = pos + orientation - perp;
	Vertices[idx].Color = particle.color;
	Vertices[idx].Normal = norm;
	idx++;
	Vertices[idx].Pos = pos - perp;
	Vertices[idx].Color = particle.color;
	Vertices[idx].Normal = norm;
	idx++;
	       }
	   }
	else
       {   
	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 * ParticleSize.Width;
	vertical *= 0.5f * ParticleSize.Height;	

	view *= -1.0f;

	

	core::vector3df v0 = horizontal + vertical;
    core::vector3df v1 = horizontal - vertical;

   // create particle vertex data
	s32 idx = 0;
	for (u32 i=0; i<Particles.size(); ++i)
	{
		SParticle& particle = Particles[i];
	
		Vertices[idx].Pos = particle.pos + v0;
		Vertices[idx].Color = particle.color;
		Vertices[idx].Normal = view;
		idx++;

		Vertices[idx].Pos = particle.pos + v1;
		Vertices[idx].Color = particle.color;
		Vertices[idx].Normal = view;
		idx++;

		Vertices[idx].Pos = particle.pos - v0;
		Vertices[idx].Color = particle.color;
		Vertices[idx].Normal = view;
		idx++;

		Vertices[idx].Pos = particle.pos - v1;
		Vertices[idx].Color = particle.color;
		Vertices[idx].Normal = view;
		idx++; 
	}
	   }
	// render all 
	if (Mode != EPSM_LOCAL)
	{
		core::matrix4 matid;
		driver->setTransform(video::ETS_WORLD, matid);
	}
	else driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);



	driver->setMaterial(Material);

	driver->drawIndexedTriangleList(Vertices.pointer(), Particles.size()*4,
		Indices.pointer(), Particles.size()*2);

	// for debug purposes only:
	if (DebugDataVisible)
	{
		video::SMaterial m;
		m.Lighting = false;
		driver->setMaterial(m);
		driver->draw3DBox(Box, video::SColor(0,255,255,255));
	}
}
Post Reply