Particles system

BlitzMax Forums/BlitzMax Programming/Particles system

So
After roughly read this topic http://blitzmax.com/Community/posts.php?topic=76706#860840

I'm wondering if anybody successfully created a robust particles system and would be generous enough to sharing-it ?
Or then, does anyone has good extensive documentation about particles system ?

I enjoy solution like :
- splash ( http://www.blitzmax.com/Community/posts.php?topic=75201 ) but no news about it
- Pyro ( http://pyro.fenomen-games.com/ ) unfortunately very expensive for casual games and still a lot of work to do with SDK
- HGE ( http://hge.relishgames.com/ ) but there isn't any portable SDK out of the box.

Because they came with particles editors.
I found very weird there isn't any good particles engine specific to BlitzMax.
There maybe modules like the Yahfree ( http://blitzmax.com/Community/posts.php?topic=78008#874898 ) but they often look incomplete.

I will probably develop my own particles system from what I can read here and there and specially adapt it to my game.
But as many others already discuss the question I though it would be better to share experiences on this.

This also had links with the particular slow down of using MAX2D rendering system...

Thanks

My framework has a particle system, but I'm afraid it's not free. I've used it for my last 3 games. It's very comprehensive and allows you to attached special user defined movement and action functions to each particle, and difference phases so their movement is scripted etc. It doesn't have an editor though - it's on my to do list ;-)

Hi Grey Alien
Your framework look very interesting but unfortunately I have already develop my own framework and since I'm working on it for a few month I won't be able to switch.
I was also thinking about a more open-source or community usage of this particles system as it's something many games require.

By the way, does your framework using some kind of rendering methods that work better than the Max2D one ?
I'm asking this since the current "bottleneck" of my game is about rendering.

Any sprite rendering system is really just a particle system. Okay so particles sometimes are used to do certain effects or whatever but let's face it, they're just sprites with a few bells and whistles which could apply to any sprite in the game. In my engine I'm just creating a single mass particle system and using it for all graphics objects.

It's not totally false but I'm very interested by how many bells and whistles we can add to control in the best condition those sprites :)

Said like this even a physics engine is trivial since it's only "things moving around".

There are interesting things to do with particles system for examples :
- Emiters (Line, area, point,...)
- Orientation (Random, Fixed, Aligned,..)
- Z-order
- Life Time
- Quantity
- Size
- Speed
- Spin
- Forces
- Color/Alpha Life
- Emission (Force, Angle, Arc, Random Motion,...)
- Timed evolution
- Sub-Emiters
- Collisions
- ...

Internal max2d rendering is fine as is. If you are experiencing bottle necks on decent systems then your code is at fault, or you're simply trying to render too many particles. Yes there are a few ways you can speed rendering up, which works on older machines. But the need for this is shrinking rapidly.

One trick I'm using is to create a sprite image that looks like there is more than 1 particle in it, then when rendered with a few other similar objects, the overall look appears to have much more visual activity. ;)

...which all apply to pretty much any sprite, so you might just as well make a fancy sprite engine and call it a particle system.

@JGOware
My code seems fine.
I'm working with multiple viewport (and a rough culling system) to split the screen in 4.
It's not things are slow it's just that rendering stuff take most of the time and I know this work could be speed up, for example with a pipeline system or something.
This would allow more things on the screen which isn't a bad idea :)

@ImaginaryHuman
... So well let call this a fancy sprite engine then.
This isn't very helpful if I'm searching a particles system with that key words but who knows ^^

Yep, the basic Max2D rendering code is quite inefficient, which is why you'll find that is a major bottleneck.
The problem is that the code has needed to be abstracted in such a way as to make it more flexible.
If you were to deal directly with the graphics backend, and avoid all the intermediate transformations etc that are made on each call, you would get more through-put.

@Armitage 1982: No special rendering on my system, although I have in place some code form a customer that uses single surface textures and also some render pipeline caching stuff that gives a speed boost but still in all practical terms it's about 10%. I haven't bothered to use Particle Pooling yet, may do in the future. Also my particles are flexible with many purposes, so of course if you have a very specific purpose in mind for a particle you can cut out lots of code and speed it up.

Splash is still beeing developed. Unfortunately my day job keeps me busy (needs 150% of my time) at the moment, but better times will come. I'm sure about it!

Best regards
Jake

There's the Smash Particle Engine which comes with the editor and code.
It doesn't use any funky low-level bypasses just Bmax commands and seems to do OK.
The best Particle system information I have seen is from 2D Special Effects in DirectX which also includes editor information.
<edit> There were 3 systems which provided source code at one point :
ParticleDreams frpom Dreamora but source was removed due to lack of feedback.
ParticleSky from Suco-X but the link is dead
ParticleEngine from Filax but can't find it anymore. There was a suggestion of ParticleEngine2 but not heard much recently.

"Yep, the basic Max2D rendering code is quite inefficient..."

100% disagree. And so would a few games such as "Bullet Candy", "Annihilation", etc. Granted, there is no internal batching, etc, but again, this is really only an issue if you want a zillion things rendered on an older integrated system. And if you're wanting to render 1000's of particles, sprites, etc, at 60fps or better, you're going to need modern hardware anyway.

One observation I can make is, I've seen several Bmax demos/games using fairly large sprites on high resolution screens. Go down to 800x600 and use smaller sprites and you will get better frame rates with a small loss in visual "pizzazz".

Relatively speaking the Max2D code is okay speed but it can get quite a bit faster.

There once was internal batching and all it made was make the whole thing slower actually.
On any real 3d cards, the OpenGL driver will already lead to batching like behavior through the driver from what it seems from my benchmarks. Thought as well that it is pretty slow so I wrote an own prototype driver for Kamaya (2D game dev framework that is on hold for an undefined amount of time). The result was that it ran at the same speed as the regular OpenGL one.

the only thing thats slow is the DX7 driver but that is not BMs fault. Back in DX7 days, NVIDIA and ATI didn't have high end monsters and the higher end monsters just didn't get optimized DX7 drivers anymore. For intel its even worse: back in DX7 days they did not have any 3D card at all. they didn't reach DX7 GPU capability level until GMA900 at all.

Using things like vertex arrays, vertex buffers and the like, you can make things faster.

Still, even on modest desktops from my own testing, you can get hundreds of sprites zipping around at 20-30fps. On more modern hardware, you can get several 1000 at 60fps. Again...I don't think it's that much of an issue. Are there faster solutions? Sure... DX9 for instance and the Sprite Object is very fast and once an official driver for Bmax /DX9 using the Sprite Object is available, we'll have a substantial speed increase. (The DX9 Sprite Object supports batching as well.) I would assume this is in the works. ? ?

I think Max is pretty quick if you are intelligent about how you use it, like stick to the smallest pow2 size you can etc and don't fill the entire screen with particles etc. Plus write good code. You can definitely make a good game in Bmax that makes big money. At the end of the day most people would probably have more fun if they just got and made a game and didn't worry too much about a 10% speed increase by twiddling with the API or changing language imho.

Using things like vertex arrays, vertex buffers and the like, you can make things faster.

Hmmm... let's not start all that again.

Thanks for the Links TonyG
Don't know why all of these particles systems get lost though...
Sound's very handy to have a fast pre-made one.

Well like I said I started my own Particles system and add the needed math to get something good.
For the moment around 1000 particles (+ a platform game without game logic) run nicely.
I will probably optimize it a bit if needed when it's done.

Armitage, if you're interested, here's the source of my particle engine:

particles.bmx
SuperStrict

Rem
bbdoc:	Yahfree's particle engine
about:	library of functions to handle particles
EndRem
Module Yah.particles

ModuleInfo "Name: Yahfree's particle engine"
ModuleInfo "Description: engine for handling particles"
ModuleInfo "License: Public Domain"
ModuleInfo "Author: Yahfree"

Import BRL.Map
Import BRL.LinkedList
Import BRL.D3D7Max2D
Import BRL.Random

Import "Include/cParticles.cpp"
Include "Include/bParticles.bmx"


Include/bParticles.bmx
Private

'Wrap functions from the particle C++ code
Extern "C"
	Function cCreateParticle:Byte Ptr(_x:Float, _y:Float, _dir:Int = 0, _speed:Float = 0, frames:Int = 1, _fade:Int = False, _autorot:Int = False, _z:Int = 0)
	Function cUpdateParticle(p:Byte Ptr, ax:Float, ay:Float, fx:Float, fy:Float)
	Function cDeleteParticle(p:Byte Ptr)
	Function cPX:Float(p:Byte Ptr)
	Function cPY:Float(p:Byte Ptr)
	Function cPA:Float(p:Byte Ptr)
	Function cPR:Int(p:Byte Ptr)
	Function cPLife:Int(p:Byte Ptr)
End Extern


'TParticle type, basicly a wrapper w/ drawing functions for the class counterpart.
Global ParticleList:TList = CreateList()

Type TParticle
	Field img:TImage 'particle img...
	Field cBuddy:Byte Ptr 'the handle for our c++ counterpart.
	Field z:Int 'an 'ID' that we call on to give us seperate drawing abilities
	
	Method Create:TParticle(_x:Float, _y:Float, _img:TImage, _dir:Int = 0, _speed:Float = 0, frames:Int = 1, _fade:Int = False, _autorot:Int = False, _z:Int = 0)
		'create a "c" particle and give it's handle to the cbuddy variable
		cBuddy = cCreateParticle(_x, _y, _dir, _speed, frames, _fade, _autorot, _z)
		'Add this object to the particle list
		ListAddLast(ParticleList,Self)
		'Add some given arguments to the object
		img = _img
		z = _z
	End Method
	
	Method Update(addx:Float, addy:Float, forcex:Float=0, forcey:Float=0)
		'Update the "c" particle
		cUpdateParticle(cBuddy, addx, addy, forcex, forcey)
		'Grab the new x/y positions of the "c" particle(so we only have to use this function once per loop)
		Local x:Float = cPX(cBuddy)
		Local y:Float = cPY(cBuddy)
		
		'check for conditions for destroying a particle
		If cPLife(cBuddy) = 0 Or x > GraphicsWidth()+ImageWidth(img) Or x < 0-ImageWidth(img) Or y > GraphicsHeight()+ImageHeight(img) Or y < 0-ImageHeight(img)
			Destroy()
			Return
		End If
		
		'Draw the given img at the "c" Particle's locations 
		SetAlpha cPA(cBuddy)
		SetRotation cPR(cBuddy)
		DrawImage img,x,y
		SetAlpha 1
		SetRotation 0
	End Method
	
	Method Destroy()
		ParticleList.Remove(Self) ' destroy bmax particle
		cDeleteParticle(cBuddy) 'destroy C++ counterpart
	End Method
	
End Type

Public

Rem
bbdoc: ParticleExplosion creator
about: Creates a particle explosion at X and Y with Image with a certain N of particles, that lasts a certain amount of Frames and can have a SpeedMultiplyer, and a Z location(to specify where rendered)
returns: Nothing
EndRem
Function ParticleExplosion(_x:Float, _y:Float, image:TImage, n:Int, frames:Int, speed_multiplyer:Float = 0, ar:Int = False, _z:Int = 0)
	Local speed:Float
	
	For Local i:Int = 1 To n
		Local dir:Int = Rand(0, 359)'random directions
		
		If speed_multiplyer
			speed = Rnd(0.08*speed_multiplyer, 1.5*speed_multiplyer) 'multiplie base speed by multiplier
		Else
			speed = Rnd(0.08, 1.5) 'base speed if no multiplier
		End If
		
		Local part:TParticle = New TParticle.Create(_x, _y, image, dir, speed, frames - 10 + Rand((frames*1.40)), True, ar, _z)
	Next
	
End Function

Rem
bbdoc: Particle update routine
about: Updates particles at said Z level, you can add a optional force routine for things such as gravity.
returns: Nothing
EndRem
Function UpdateParticlesZ(z:Int = 0, addx:Float = 0, addy:Float = 0, forcex:Float = 0, forcey:Float = 0)	
	For Local UpdateP:TParticle = EachIn ParticleList
		If UpdateP.z = z
			UpdateP.Update(addx, addy, forcex, forcey)
		End If
	Next
End Function

Rem
bbdoc: Particle creator
about: Creates a particle at said point with said attributes
returns: Nothing
EndRem
Function EmitParticle(_x:Float, _y:Float, image:TImage, frames:Int, fade:Int = True, dir:Int = 0, speed:Float = 0, ar:Int = False, _z:Int = 0)

	Local part:TParticle = New TParticle.Create(_x, _y, image, dir, speed, frames, fade, ar, _z)

End Function

Rem
bbdoc: Clear particlesZ function
about: Clears all particles on the given Z level.
returns: Nothing
End Rem
Function ClearParticlesZ(z:Int = 0)
	For Local delp:TParticle = EachIn ParticleList
		If delp.z = z
			delp.Destroy
		End If
	Next
End Function

Rem
bbdoc: Particle count function
about: Returns the particle count
returns: The particle count
End Rem
Function ParticleCount:Int()
	Return ParticleList.Count()
End Function


Include/cParticles.cpp
#include <math.h>
#define DEG_TO_RAD 0.0174532925199432957692369076848861

//Functions to grab a Radian sin/cos not in degrees.
double Sin2( double ang ){
	return sin( ang*DEG_TO_RAD );
}
double Cos2( double ang ){
	return cos( ang*DEG_TO_RAD );
}

// All the stuff that can be accessed in bmax
extern "C" {

    // our particle class
    class cParticle {
    public:
          // all the variables associated with a particle
          float x;
          float y;
          float a;
          int r;
          float dx;
          float dy;
          float da;
          int maxdistance;
          float speed;
          int dir;
          int fade;
          int z;
          
          // constructor (AKA Create())
          cParticle(float, float, int, float, int, bool, bool, int);
          // destructor have not found a use yet
          ~cParticle();
          
          //Update particle
          void Update(float, float, float, float); 
    };
    
    // Our class functions redefined:
           
    // constuctor
    cParticle::cParticle(float _x, float _y, int _dir = 0, 
                               float _speed = 0, 
                          int frames = 1, bool _fade = false, 
                          bool _autorot = false, int _z = 0) {
                
  		z = _z;
		x = _x;
		y = _y;
		speed = _speed;
		dir = _dir;
		dx = (Sin2(dir) * speed); 
		dy = (-Cos2(dir) * speed);
		da = 1.0 / frames;
		a = 1.0;
		maxdistance = frames;
		if(dir > 0&&_autorot > 0){
			r = dir;
		}

		if(_fade > 0) {
			fade = true;
        }
                               
    
    }
    //destructor
    cParticle::~cParticle() {
        
        // code for destructor
    }      
    
             
    void cParticle::Update(float ax,float ay,float fx, float fy) {
		dx = dx+fx;
		dy = dy+fy;
		x=x+dx+ax;
		y=y+dy+ay;
		if( fade == true ) { a=a-da; }
		maxdistance--;
    }
    
    
    // mainstream functions:
                 
    // Wrap function to create a particle
    cParticle *cCreateParticle(float _x, float _y, int _dir = 0, 
                               float _speed = 0, 
                          int frames = 1, bool _fade = false, 
                          bool _autorot = false, int _z = 0) {
         cParticle *p; // particle that we will return
         p = new cParticle(_x, _y, _dir, 
                           _speed, frames, 
                           _fade, 
                           _autorot, _z); // basicly shove the arguments of this function into a constructor
         return p;
    }
    
    //Wrap function to delete a particle
    void cDeleteParticle(cParticle *p) {
         if (p != 0) { delete p; }
    }
    
    //Wrap function to update a particle
    void cUpdateParticle(cParticle *p, float ax, float ay, float fx, float fy){
         if (p != 0){ p->Update(ax, ay, fx, fy); }
    }
    
    //functions to get various states of a particle
    float cPX(cParticle *p) {
         if (p != 0){ return p->x; }
    }
    float cPY(cParticle *p) {
         if (p != 0){ return p->y; }
    }
    float cPA(cParticle *p) {
         if (p != 0){ return p->a; }
    }
    int cPR(cParticle *p) {
         if (p != 0){ return p->r; }
    }
    int cPLife(cParticle *p) {
        if (p != 0){ return p->maxdistance; }
    }
}



The slowest part of the particle engine as of now is the constant allocation/delocation of the memory for each particle, this could be drastically improved with arrays, but I had to many problems with doing so, so I dropped that for now, the particle engine can handle upwards of 4500+ particles before slowing down(at least on my system)

"the particle engine can handle upwards of 4500+ particles before slowing down(at least on my system)"

Depending on GPU, your mileage will vary ofcourse. ;)

Why the need for C??????

well I figure it's slightly faster doing all the updating in C++, plus I was learning C++ at the time.

What spec machine and graphic card do you have, getting the 4500 objects, and what size are the objects, and what resolution is the screen, and at what hertz rate?

intel p4 3.0GHz processer, 3gb ram, windows xp, and a NVIDIA 9600GT gfx card

wow..that's not typical of a casual 2d gamer. ;)

Try to convert an old blitz3D particle engine ?

Wowza. So your gfx card is about 5 times faster than mine, which means your 4500 turns into around 1000 objects, which does not sound very fast?

dunno, I can write a stress test app if you want to test it?

The main bottle neck is GPU not so much CPU. Then you have to consider the size of each particle, if you're scaling, rotating, alpha, even blend mode makes a difference. Squeezing a little bit of speed from an array system or compiled C code helps, but perhaps not much. I have an old computer with a good Geforce card that has frame rates faster than a brand spanking new Vista Laptop (DX or GL mode).

I'm guessing my MGE system v.s. YahFree or ImaginaryHuman's or Grey's system would all come very close, because we're all at the mercy of the built in gpu engine.

The only way one of our particle engines could really outperform another substantially would be if we coded our own low level batch system in DX7 or OpenGL and even that may not yield much better results depending what you're doing. And if you need that much of a performance increase you're probably dealing with a hardcore audience who will more than likely have a modern gpu anyway.

I've downloaded particle demos, sprite demos, game demos, in Blitzmax the past few months and not one has really out performed the other as far as rendering speed. I did see a few OpenGL only demos that did some cool stuff, but who wants to code a game that requires a specific GPU Driver?

Thanks Yahfree
Well at the moment I was telling you I was working on my own particle system I finished it a few hour after.
On my AMD X2 2ghz and 1go ram Nvidia 7600 GS my platform game running a full blitzmax particles system at 6000 particles without fps drop (currently 60 fps).

I've add a tweening start/end function for every parameters (arc, angles, scale, spawn, speed, color, gravity, frame, etc.) with variance, random values and for the moment only use a linear motion. I can tell that BlitzMax is very robust so any optimisation of that kind won't be necessary for casual games.

I have something similar to the HGE particle system which is really enough for me.

Yeah either you would aim to make it as fast as possible to work better on higher end systems or just in general so that people with slower computers get a better framerate, which is still attractive. My particle engine is in pieces at the moment so I can't really test or compare it but I hope it will be efficient. When you're dealing with large numbers of things, small speedups on the micro scale can turn into big speedups on the macro scale.

For casual games or games with less effects I guess it doesn't really matter so much how fast it is so long as it's fast enough for a reasonable amount of action like most retro games or casual games. I'm aiming for 10's of thousands of particles on my system as well as every particle colliding with every other particle, so we'll see how it goes.

"I'm aiming for 10's of thousands of particles on my system as well as every particle colliding with every other particle, so we'll see how it goes."

Can't wait for the benchmark test for that one. ;)

You right ^^
Also, The time I complete my game the middleware computer will be far more powerful than the one i'm using right now for developing it :)
They already talk about quadcore and so on...

10k particles with collision seem challenging though !

All you peeps coding a zillion things moving around, for pete's sake, test it on low end gpu's just for the fun of it. Nothing like seeing something that was running at 200fps or greater run at 20fps or less. A real eye opener in some cases. ;)

True. My GPU is an ATI X1600 128mb, about twice the speed of an NVidea GeForce 4 FX at only 2 billion texels/sec, compared with some of the 20-80 billion/sec at the high end, it's not exactly state of the art but is some 10-40 times slower and is roughly what you get on the lowest end iMac these days.

I'm not sure I'll have 10's of thousands of particles on the viewable screen area at once, but they will all be still moving and animating off-screen with maybe a few thousand visible at a time. But collisions and stuff still needs to happen for all objects all the time no matter where they are.

If you think about physics, in order for a physics model to be stable and continuous you have to simulate the interactions of all objects everywhere all the time, not just in some isolated local area. That means handling thousands of off-screen objects in addition to what you can see.

Yeah I test my stuff on a Vista Laptop and an old ME machine to see if it's stinky and slow and if so I tweak the particle count (down).