From 880653d31a8f1ff8384fdbc75b84934bceecfdb8 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Sat, 18 Nov 2000 06:43:49 +0000 Subject: Initial revision --- demos/RonsDemos/particle.java | 613 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 613 insertions(+) create mode 100644 demos/RonsDemos/particle.java (limited to 'demos/RonsDemos/particle.java') diff --git a/demos/RonsDemos/particle.java b/demos/RonsDemos/particle.java new file mode 100644 index 0000000..2369b00 --- /dev/null +++ b/demos/RonsDemos/particle.java @@ -0,0 +1,613 @@ +/** + * @(#) particle.java + * @(#) author: Nate Robins (converted to Java by Ron Cemer) + */ + +/* + particle + Nate Robins, 1997 + + An example of a simple particle system. + */ + +import java.applet.*; +import java.awt.*; +import java.awt.event.*; +import java.lang.*; +import java.util.*; +import java.io.*; +import java.util.*; +import gl4java.GLContext; +import gl4java.awt.GLAnimCanvas; +import gl4java.applet.SimpleGLAnimApplet1; + +public class particle extends SimpleGLAnimApplet1 +{ + private static final int PS_WATERFALL = 0, PS_FOUNTAIN = 1; + private static Random random = null; + + /* Initialize the applet */ + + + public void init() + { + super.init(); + Dimension d = getSize(); + canvas = new particleCanvas(d.width, d.height); + add("Center", canvas); + canvas.requestFocus(); + random = new Random(System.currentTimeMillis()); + } + + + private class PSsphere + { + protected float x, y, z; + protected float radius, radiussquared; + + public PSsphere(float x, float y, float z, float radius) + { + this.x = x; + this.y = y; + this.z = z; + this.radius = radius; + radiussquared = radius*radius; + } + } + + private class PSparticle + { + private final float PS_GRAVITY = -9.8f; + + protected float position[] = new float[3]; /* current position */ + protected float previous[] = new float[3]; /* previous position */ + protected float velocity[] = new float[3]; /* velocity (magnitude & direction) */ + protected float dampening = 0.0f; /* % of energy lost on collision */ + protected boolean alive = false; /* is this particle alive? */ + + public PSparticle() + { + velocity[0] = previous[0] = position[0] = 0.0f; + velocity[1] = previous[1] = position[1] = 0.0f; + velocity[2] = previous[2] = position[2] = 0.0f; + dampening = 0.0f; + alive = false; + } + + public PSparticle(float dt, int type) + { + if (type == PS_WATERFALL) + { + velocity[0] = (random.nextFloat()-0.5f); + velocity[1] = 0.0f; + velocity[2] = 0.5f*random.nextFloat(); + position[0] = 0.0f; + position[1] = 2.0f; + position[2] = 0.0f; + dampening = 0.45f*random.nextFloat(); + } + else if (type == PS_FOUNTAIN) + { + velocity[0] = 2.0f*(random.nextFloat()-0.5f); + velocity[1] = 6.0f; + velocity[2] = 2.0f*(random.nextFloat()-0.5f); + position[0] = 0.0f; + position[1] = 0.0f; + position[2] = 0.0f; + dampening = 0.35f*random.nextFloat(); + } + previous[0] = position[0]; + previous[1] = position[1]; + previous[2] = position[2]; + alive = true; + psTimeStep(2.0f*dt*random.nextFloat()); + } + + public void psTimeStep(float dt) + { + if (!alive) return; + + velocity[1] += PS_GRAVITY*dt; + + previous[0] = position[0]; + previous[1] = position[1]; + previous[2] = position[2]; + + position[0] += velocity[0]*dt; + position[1] += velocity[1]*dt; + position[2] += velocity[2]*dt; + } + + /* psBounce: the particle has gone past (or exactly hit) the + ground plane, so calculate the time at which the particle + actually intersected the ground plane (s). essentially, this + just rolls back time to when the particle hit the ground plane, + then starts time again from then. + + - - o A (previous position) + | | \ + | s \ o (position it _should_ be at) - + t | \ / | t - s + | - ------X-------- - + | \ + - o B (new position) + + A + V*s = 0 or s = -A/V + + to calculate where the particle should be: + + A + V*t + V*(t-s)*d + + where d is a damping factor which accounts for the loss + of energy due to the bounce. */ + public void psBounce(float dt) + { + if (!alive) return; + + /* since we know it is the ground plane, we only need to + calculate s for a single dimension. */ + float s = -previous[1]/velocity[1]; + + position[0] = + (previous[0] + velocity[0]*s + velocity[0]*(dt-s)*dampening); + position[1] = + -velocity[1] * (dt-s) * dampening; /* reflect */ + position[2] = + (previous[2] + velocity[2]*s + velocity[2]*(dt-s)*dampening); + + /* damp the reflected velocity (since the particle hit + something, it lost some energy) */ + velocity[0] *= dampening; + velocity[1] *= -dampening; /* reflect */ + velocity[2] *= dampening; + } + + public void psCollide(PSsphere sphere) + { + if (!alive) return; + + float vx = position[0] - sphere.x; + float vy = position[1] - sphere.y; + float vz = position[2] - sphere.z; + float distance, distancesquared; + + // Use distancesquared and radiussquared for comparison, + // eliminating the sqrt() call unless it is absolutely + // necessary, since it is time-consuming. + distancesquared = (vx*vx + vy*vy + vz*vz); + if (distancesquared < sphere.radiussquared) + { + distance = (float)Math.sqrt(distancesquared); + position[0] = sphere.x+(vx/distance)*sphere.radius; + position[1] = sphere.y+(vy/distance)*sphere.radius; + position[2] = sphere.z+(vz/distance)*sphere.radius; + previous[0] = position[0]; + previous[1] = position[1]; + previous[2] = position[2]; + velocity[0] = vx/distance; + velocity[1] = vy/distance; + velocity[2] = vz/distance; + } + } + } + + + /* Local GLAnimCanvas extension class */ + + + private class particleCanvas extends GLAnimCanvas + implements MouseListener, MouseMotionListener, KeyListener + { + private static final float M_PI = 3.14159265359f; + + private MatrixFuncs mtxfuncs = null; + private int prevMouseX, prevMouseY; + private boolean mouseRButtonDown = false; + private float rot_matrix[] = + { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + private PSparticle[] particles = null; + private PSsphere sphere = new PSsphere(0.0f,1.0f,0.0f,0.25f); + private int num_particles = 5000; + private int living = 0; /* index to end of live particles */ + private int type = PS_WATERFALL; + private boolean use_lines = false; + private boolean do_sphere = false; + private float flow = 500.0f; + private float slow_down = 1.0f; + private int point_size = 3; + private int sphere_list = 0; + + public particleCanvas(int w, int h) + { + super(w, h); + GLContext.gljNativeDebug = false; + GLContext.gljClassDebug = false; + setAnimateFps(30.0); + + mtxfuncs = new MatrixFuncs(); + } + + public void preInit() + { + doubleBuffer = true; + stereoView = false; + } + + public void init() + { + System.out.println("init(): " + this); + reshape(getSize().width, getSize().height); + + setInitialRotation(); + + particles = new PSparticle[num_particles]; + for (int i = 0; i < num_particles; i++) + particles[i] = new PSparticle(); + + sphere_list = gl.glGenLists(1); + gl.glNewList(sphere_list,GL_COMPILE); + gl.glColor3ub((byte)0, (byte)255, (byte)128); + int qobj = glu.gluNewQuadric(); + glu.gluQuadricOrientation(qobj,GLU_OUTSIDE); + glu.gluQuadricNormals(qobj,GLU_SMOOTH); + glu.gluQuadricTexture(qobj,false); + glu.gluSphere(qobj,sphere.radius, 16, 16); + glu.gluDeleteQuadric(qobj); + gl.glEndList(); + + glj.gljCheckGL(); + + addMouseListener(this); + addMouseMotionListener(this); + addKeyListener(this); + } + + public void doCleanup() + { + System.out.println("destroy(): " + this); + removeMouseListener(this); + removeMouseMotionListener(this); + removeKeyListener(this); + } + + public void reshape(int width, int height) + { + gl.glViewport(0,0,width,height); + + float black[] = { 0, 0, 0, 0 }; + + gl.glMatrixMode(GL_PROJECTION); + gl.glLoadIdentity(); + glu.gluPerspective(60, 1, 0.1, 1000); + gl.glMatrixMode(GL_MODELVIEW); + gl.glLoadIdentity(); + glu.gluLookAt(0, 1, 3, 0, 1, 0, 0, 1, 0); + gl.glFogfv(GL_FOG_COLOR, black); + gl.glFogf(GL_FOG_START, 2.5f); + gl.glFogf(GL_FOG_END, 4f); + gl.glEnable(GL_FOG); + gl.glFogi(GL_FOG_MODE, GL_LINEAR); + gl.glPointSize(point_size); + gl.glEnable(GL_POINT_SMOOTH); + gl.glEnable(GL_BLEND); + gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl.glEnable(GL_COLOR_MATERIAL); + gl.glEnable(GL_DEPTH_TEST); + gl.glEnable(GL_LIGHT0); + + timedelta(); + } + + public void display() + { + if (glj.gljMakeCurrent() == false) return; + + gl.glPushMatrix(); + gl.glMultMatrixf(rot_matrix); + + // Update particles. + + float dt = timedelta(); + float truedt = dt; + + /* slow the simulation if we can't keep the frame rate up + around 10 fps */ + if (dt > 0.1f) + slow_down = 0.75f; + else if (dt < 0.1f) + slow_down = 1.0f; + dt *= slow_down; + + /* resurrect a few particles */ + for (int i = 0; i < flow*dt; i++) + { + particles[living] = new PSparticle(dt,type); + living++; + if (living >= num_particles) living = 0; + } + + for (int i = 0; i < num_particles; i++) + { + particles[i].psTimeStep(dt); + /* collision with sphere? */ + if (do_sphere) particles[i].psCollide(sphere); + /* collision with ground? */ + if (particles[i].position[1] <= 0.0f) + particles[i].psBounce(dt); + /* dead particle? */ + if ( (particles[i].position[1] < 0.1f) + && (Math.abs(particles[i].velocity[1]) < 0.2f) ) + particles[i].alive = false; + } + + // Draw the scene. + + gl.glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + gl.glEnable(GL_LIGHTING); + if (do_sphere) + { + gl.glPushMatrix(); + gl.glTranslatef(sphere.x, sphere.y, sphere.z); + gl.glCallList(sphere_list); + gl.glPopMatrix(); + } + gl.glDisable(GL_LIGHTING); + + gl.glColor3ub((byte)0, (byte)128, (byte)255); + gl.glBegin(GL_QUADS); + gl.glVertex3f(-2f, 0f, -2f); + gl.glVertex3f(-2f, 0f, 2f); + gl.glVertex3f(2f, 0f, 2f); + gl.glVertex3f(2f, 0f, -2f); + gl.glEnd(); + + float r, g; + if (!use_lines) + { + gl.glBegin(GL_POINTS); + for (int i = 0; i < num_particles; i++) + { + if (!particles[i].alive) continue; + r = particles[i].position[1]/2.1f*255f; + g = 128.0f+(r*0.5f); + gl.glColor3ub((byte)r, (byte)g, (byte)255); + gl.glVertex3fv(particles[i].position); + } + gl.glEnd(); + } + else + { + gl.glBegin(GL_LINES); + for (int i = 0; i < num_particles; i++) + { + if (!particles[i].alive) continue; + r = particles[i].previous[1]/2.1f*255f; + g = 128.0f+(r*0.5f); + gl.glColor3ub((byte)r, (byte)g, (byte)255); + gl.glVertex3fv(particles[i].previous); + r = particles[i].position[1]/2.1f*255f; + g = 128.0f+(r*0.5f); + gl.glColor3ub((byte)r, (byte)g, (byte)255); + gl.glVertex3fv(particles[i].position); + } + gl.glEnd(); + } + + gl.glPopMatrix(); + + glj.gljSwap(); + glj.gljCheckGL(); + glj.gljFree(); + + //if (!isSuspended()) repaint(); // Animate at full speed. + } + + /* timedelta: returns the number of seconds that have elapsed + since the previous call to the function. */ + private long timedelta_prev = 0L; + private float timedelta() + { + long current = System.currentTimeMillis(); + if (timedelta_prev == 0L) timedelta_prev = current; + long delta = current-timedelta_prev; + timedelta_prev = current; + return ((float)delta) * 0.001f; + } + + // Reset the rotation matrix to the default view. + private void setInitialRotation() + { + for (int i = 0; i < 16; i++) + rot_matrix[i] = ((i % 5) == 0) ? 1.0f : 0.0f; + } + + private void fixRotationMatrix() + { + // Fix any problems with the rotation matrix. + rot_matrix[3] = + rot_matrix[7] = + rot_matrix[11] = + rot_matrix[12] = + rot_matrix[13] = + rot_matrix[14] = 0.0f; + rot_matrix[15] = 1.0f; + float fac; + if ((fac = (float)Math.sqrt + ((rot_matrix[0]*rot_matrix[0]) + + (rot_matrix[4]*rot_matrix[4]) + + (rot_matrix[8]*rot_matrix[8]))) != 1.0f) + { + if (fac != 0.0f) + { + fac = 1.0f/fac; + rot_matrix[0] *= fac; + rot_matrix[4] *= fac; + rot_matrix[8] *= fac; + } + } + if ((fac = (float)Math.sqrt + ((rot_matrix[1]*rot_matrix[1]) + + (rot_matrix[5]*rot_matrix[5]) + + (rot_matrix[9]*rot_matrix[9]))) != 1.0f) + { + if (fac != 0.0f) + { + fac = 1.0f/fac; + rot_matrix[1] *= fac; + rot_matrix[5] *= fac; + rot_matrix[9] *= fac; + } + } + if ((fac = (float)Math.sqrt + ((rot_matrix[2]*rot_matrix[2]) + + (rot_matrix[6]*rot_matrix[6]) + + (rot_matrix[10]*rot_matrix[10]))) != 1.0f) + { + if (fac != 0.0f) + { + fac = 1.0f/fac; + rot_matrix[2] *= fac; + rot_matrix[6] *= fac; + rot_matrix[10] *= fac; + } + } + } + + // Methods required for the implementation of MouseListener + public void mouseEntered( MouseEvent evt ) + { + Component comp = evt.getComponent(); + if( comp.equals(this ) ) + { + requestFocus(); + } + } + + public void mouseClicked( MouseEvent evt ) + { + Component comp = evt.getComponent(); + if( comp.equals(this ) ) + { + requestFocus(); + } + } + + public void mouseExited( MouseEvent evt ) + { + } + + public void mousePressed( MouseEvent evt ) + { + prevMouseX = evt.getX(); + prevMouseY = evt.getY(); + if ((evt.getModifiers() & evt.BUTTON3_MASK) != 0) + { + mouseRButtonDown = true; + evt.consume(); + } + } + + public void mouseReleased( MouseEvent evt ) + { + if ((evt.getModifiers() & evt.BUTTON3_MASK) != 0) + { + mouseRButtonDown = false; + evt.consume(); + } + } + + // Methods required for the implementation of MouseMotionListener + public void mouseDragged( MouseEvent evt ) + { + int x = evt.getX(); + int y = evt.getY(); + float thetaX = (float)(x-prevMouseX)*(360.0f/(float)getSize().width); + float thetaY = (float)(prevMouseY-y)*(360.0f/(float)getSize().height); + float mtxbuf[] = new float[16]; + float mtxbuf2[] = new float[16]; + + prevMouseX = x; + prevMouseY = y; + if ( (thetaX != 0.0f) || (thetaY != 0.0f) ) + { + mtxfuncs.rotateAroundY(((float)thetaX),mtxbuf); + if (mouseRButtonDown) + mtxfuncs.rotateAroundZ(thetaY,mtxbuf2); + else + mtxfuncs.rotateAroundX(-thetaY,mtxbuf2); + mtxfuncs.multiplyMatrices(mtxbuf2,mtxbuf,mtxbuf); + mtxfuncs.multiplyMatrices(rot_matrix,mtxbuf,rot_matrix); + fixRotationMatrix(); + } + evt.consume(); + } + + public void mouseMoved( MouseEvent evt ) + { + } + + // Methods required for the implementation of KeyListener + public void keyPressed(KeyEvent e) + { + } + + public void keyReleased(KeyEvent e) + { + } + + public void keyTyped(KeyEvent e) + { + if (glj.gljMakeCurrent() == false) return; + switch ((char)e.getKeyChar()) + { + case 'W': + case 'w': + type = PS_WATERFALL; + break; + case 'F': + case 'f': + type = PS_FOUNTAIN; + break; + case 'S': + case 's': + do_sphere = !do_sphere; + break; + case 'L': + case 'l': + use_lines = !use_lines; + break; + case 'P': + point_size++; + gl.glPointSize(point_size); + break; + case 'p': + point_size--; + if (point_size < 1) point_size = 1; + gl.glPointSize(point_size); + break; + case '+': + flow += 100; + if (flow > num_particles) flow = num_particles; + System.out.println(flow + " particles/second"); + break; + case '-': + flow -= 100; + if (flow < 0) flow = 0; + System.out.println(flow + " particles/second"); + break; + case 'R': + case 'r': + setInitialRotation(); + break; + } + glj.gljFree(); + } + } +} -- cgit v1.2.3