Re: Particle systems outside of Second Life

"Kelly Jones" <[email protected]>
Newsgroups gmane.org.user-groups.nmlug
Message-ID <[email protected]>
Thanks to everyone who helped. In case anyone's interested, I wrote my
own "particle system" (attached) using GLUT. "gcc -lglut particleman.c
-o particleman" should compile it if you have GLUT installed.

It's fairly basic, so suggestions/improvements are welcomed. With the
default settings, it looks almost entirely unlike a stream of water
fighting a fire :)

-- 
We're just a Bunch Of Regular Guys, a collective group that's trying
to understand and assimilate technology. We feel that resistance to
new ideas and technology is unwise and ultimately futile.

On 9/4/07, Kelly Jones <[email protected]> wrote:
> When playing secondlife.com, I got addicted to particle systems:
>
> http://wiki.secondlife.com/wiki/LlParticleSystem
>
> because they can so realistically model fire, water, clouds, etc.
>
> But logging into Second Life just to create a particle system is
> inefficient/laggy (+ there's always someone there who wants to chat,
> sheesh!).
>
> What's a good, free, open source particle system generator/displayer
> for Linux?
>
> I was half-tempted to use Perl script and povray to "roll my own", but
> there has to be a better solution?

_______________________________________________
NMLUG mailing list
[email protected]
http://lists.b9.com/cgi-bin/mailman/listinfo/nmlug
particleman.c (application/octet-stream, 5.9 KB)
// attempts to create a simple particle system
// TODO: alpha channel 'a' is currently ignored/unused

// "gcc -lglut particleman.c -o particleman" should compile me

// MORE AMBITIOUS FAILED GOAL:
// attempt to replicate Second Life's particle system function
// http://wiki.secondlife.com/wiki/LlParticleSystem 

#include <GL/glut.h>
#include <stdlib.h>

// max number of particles
#define MAXPARTICLES 64000

// set to 1 for 3-D particles, not just points (very slow on my machine)
#define SLOW 0

// a particle has 3D position, velocity, RGBA color, birth time, and size
// size=0 -> don't render this particle
// TODO: should 'life' be a property of each particle?
// TODO: add concept of acceleration? (d2x, d2y, d2z natch)
struct particle {float x,y,z,dx,dy,dz,r,g,b,a,birth,size;};

typedef struct particle PARTICLE;

// vector (useful for color, position, velocity)
struct vector {float x,y,z;};

typedef struct vector VECTOR;

// array of particles
PARTICLE particles[MAXPARTICLES];

// the interval for each redraw
float dt = 1.;

// current "high water" particle (loops after hitting max)
int highwater = 0;

// defining the particle system
float num = 1.; // particles generated per time interval
float life = 500.; // life of each particle, in time units
float size_start = 0.01; // starting size of each particle
float size_end = 0.05; // ending size of each particle
VECTOR color_start = {0.,0.,1.}; // starting color of each particle
VECTOR color_end = {1.,0.,0.}; // ending color of each particle
VECTOR dir = {0.008,0.,0}; // direction in which particles are emitted
VECTOR turb_dir = {0.002,0.004,0.00}; // randomness of direction
VECTOR emitter = {-1.,0,0}; // location of particle emitter
VECTOR gravity = {-0.00002,0,0}; // the force of gravity (or whatever)

// the start time
float t = 0.0;

// Spits out vector info w/ tag
void debugVector (VECTOR vec, char *tag) {
  printf("%s: <%f,%f,%f>\n",tag,vec.x,vec.y,vec.z);
}

// Spits out a bunch of information on a particle w/ tag string
void debugParticle (PARTICLE part, char *tag) {
  printf("%s: POS=<%f,%f,%f>, DIR=<%f,%f,%f>\n",tag,part.x,part.y,part.z,part.dx,part.dy,part.dz);
}

// update the particles
void updateParticles (void) {
  int i;
  VECTOR rand = {1.*random()/RAND_MAX-0.5,1.*random()/RAND_MAX-0.5,1.*random()/RAND_MAX-0.5};

  // handle existing particles
  for (i=0; i<MAXPARTICLES; i++) {
    // convenience variable
    float age = t-particles[i].birth;

    // if particle has exceeded lifetime, kill it (set size to 0)
    // ignore particles that already dead
    // TODO: should -1 be the "dead value" instead?
    if (age>life || particles[i].size==0) {particles[i].size=0; continue;}

    // tweak particle velocity
    particles[i].dx += gravity.x*dt;
    particles[i].dy += gravity.y*dt;
    particles[i].dz += gravity.z*dt;

    // tweak particle position
    particles[i].x += particles[i].dx*dt;
    particles[i].y += particles[i].dy*dt;
    particles[i].z += particles[i].dz*dt;

    // tweak particle size
    particles[i].size = size_start + (age/life)*(size_end-size_start);

    // tweak particle color
    particles[i].r = color_start.x + (age/life)*(color_end.x-color_start.x);
    particles[i].g = color_start.y + (age/life)*(color_end.y-color_start.y);
    particles[i].b = color_start.z + (age/life)*(color_end.z-color_start.z);

  }

  // generate new particles (starting at highwater)
  // TODO: this is wrong -- it generate num particles each time, not num*dt
  for (i=0; i<num; i++) {
    if (++highwater > MAXPARTICLES) {highwater=0;}

    // particles start at emitter location w/ direction dir
    // size/color determined by size_start and color_start
    // TODO: better way of doing this?
    // "PARTICLE part = particles[highwater];" does not work (creates a copy)

    particles[highwater].x = emitter.x;
    particles[highwater].y = emitter.y;
    particles[highwater].z = emitter.z;
    particles[highwater].dx = dir.x+rand.x*turb_dir.x;
    particles[highwater].dy = dir.y+rand.y*turb_dir.y;
    particles[highwater].dz = dir.z+rand.z*turb_dir.z;
    particles[highwater].r = color_start.x;
    particles[highwater].g = color_start.y;
    particles[highwater].b = color_start.z;
    particles[highwater].a = 0.0; // TODO: actually use alpha value
    particles[highwater].birth = t;
    particles[highwater].size = size_start; // TODO: allow 3-D size values?

  }

  // increment the time
  t += dt;
}

void display (void) {
  int i;

  // update the particles
  updateParticles();

  // clear the buffer
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // forces all translations to be local?
  glPushMatrix();

  // render the particles
  for (i=0; i<MAXPARTICLES; i++) {
    PARTICLE part = particles[i]; // convenience
   
    // skip "dead" or uncreated particles
    if (part.size == 0) {continue;}

    // render particle (reset coordinate system to 0,0,0 each time)

    if (SLOW) {
      // method 1 for rendering particles (slow)
      glPushMatrix();
      glTranslatef(part.x,part.y,part.z);
      glColor3f(part.r,part.g,part.b);
      glutSolidSphere(part.size,5,5);
      glPopMatrix();
    } else {
      glBegin(GL_POINTS);
      glColor3f(part.r,part.g,part.b);
      glVertex2f(part.x,part.y);
      // failed experiment w/ GL_POLYGON below (too slow)
      //      glVertex2f(part.x+part.size,part.y);
      //      glVertex2f(part.x+part.size,part.y+part.size);
      //      glVertex2f(part.x,part.y+part.size);
      glEnd();
    }

  }

  glPopMatrix();
  glutSwapBuffers();
}

int main(int argc, char **argv)
{
 // use double buffering + depth buffering
 glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);

 // optional: window size
 glutInitWindowSize(200,150);

 // create the window
 glutCreateWindow("Particle Man");

 // glutDisplayFunc(): what to do when display is damaged
 glutDisplayFunc(display);

 // glutIdleFunc(): what to do when application is idle
 // often same as glutDisplayFunc()
 glutIdleFunc(display);

 glutMainLoop();
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.