/** * Lesson19.java * * Author: Mattias Ekstrand * Date: 18/04/2001 * * Port of the NeHe OpenGL Tutorial (Lesson 19: "Particle Engine Using Triangle Strips") * to Java using the GL4Java interface to OpenGL. Much of the code is reused * from Darren Hodges port of Lession 9. :) * */ import java.applet.*; import java.awt.*; import java.awt.event.*; //GL4Java classes import gl4java.GLContext; import gl4java.awt.GLAnimCanvas; import gl4java.utils.textures.*; public class Lesson19 extends Applet { //Our rendering canvas //We are using GLAnimCanvas because we want the canvas //to be constantly redrawn renderCanvas canvas = null; /** * void init() * * Initialise the applet. */ public void init() { //We will use BorderLayout to layout the applet components setLayout(new BorderLayout()); //Create our canvas and add it to the center of the applet canvas = new renderCanvas(getSize().width, getSize().height); add("Center", canvas); } /** * void start() * * Start the applet. */ public void start() { //Start animating the canvas canvas.start(); } /** * void stop() * * Stop the applet. */ public void stop() { //Stop animating the canvas canvas.stop(); } /** * void destroy() * * Destroy the applet. */ public void destroy() { //Stop animating the canvas canvas.stop(); //Destroy the canvas canvas.destroy(); } static final float[][] colors = { // Rainbow Of Colors {1.0f,0.5f,0.5f},{1.0f,0.75f,0.5f},{1.0f,1.0f,0.5f},{0.75f,1.0f,0.5f}, {0.5f,1.0f,0.5f},{0.5f,1.0f,0.75f},{0.5f,1.0f,1.0f},{0.5f,0.75f,1.0f}, {0.5f,0.5f,1.0f},{0.75f,0.5f,1.0f},{1.0f,0.5f,1.0f},{1.0f,0.5f,0.75f} }; private class renderCanvas extends GLAnimCanvas implements KeyListener { static final int RAND_MAX = 32767; static final int MAX_PARTICLES = 500; boolean rainbow = true; // Rainbow Mode? boolean sp = false; //Spacebar Pressed? boolean rp = false; // Return Key Pressed? float slowdown=2.0f; // Slow Down Particles float xspeed; // Base X Speed (To Allow Keyboard Direction Of Tail) float yspeed; // Base Y Speed (To Allow Keyboard Direction Of Tail) float zoom=-40.0f; // Used To Zoom Out int col; // Current Color Selection int delay; // Rainbow Effect Delay int[] texture = new int[1]; // Storage For Our Particle Texture Particle[] particles = new Particle[MAX_PARTICLES]; // Particle Array (Room For Particle Info) public renderCanvas(int w, int h) { super(w, h); //Registers this canvas to process keyboard events addKeyListener(this); } public void preInit() { //We want double buffering doubleBuffer = true; //But we dont want stereo view stereoView = false; } public float rand() { return ((float) Math.random()*RAND_MAX); } public void LoadTextures() { PngTextureLoader texLoader=new PngTextureLoader(gl, glu); texLoader.readTexture(getCodeBase(), "data/star.png"); if(texLoader.isOk()) { gl.glGenTextures(1, texture); //Create Linear Filtered Texture gl.glBindTexture(GL_TEXTURE_2D, texture[0]); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gl.glTexImage2D(GL_TEXTURE_2D, 0, 3, texLoader.getImageWidth(), texLoader.getImageHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, texLoader.getTexture()); } } public void init() { LoadTextures(); gl.glShadeModel(GL_SMOOTH); //Enables Smooth Color Shading gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //This Will Clear The Background Color To Black gl.glClearDepth(1.0f); //Enables Clearing Of The Depth Buffer gl.glDisable(GL_DEPTH_TEST); //Disables Depth Testing gl.glEnable(GL_BLEND); //Enable Blending gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Type Of Blending To Perform gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST); // Really Nice Perspective Calculations gl.glHint(GL_POINT_SMOOTH_HINT,GL_NICEST); // Really Nice Point Smoothing gl.glEnable(GL_TEXTURE_2D); // Enable Texture Mapping gl.glBindTexture(GL_TEXTURE_2D,texture[0]); // Select Our Texture for (int i=0; i < MAX_PARTICLES; i++) { // Initials All The Textures particles[i] = new Particle(); particles[i].active = true; // Make All The Particles Active particles[i].life = 1.0f; // Give All The Particles Full Life particles[i].fade = rand()%100/1000.0f+0.003f; // Random Fade Speed particles[i].r=colors[i%12][0]; // Select Red Rainbow Color particles[i].g=colors[i%12][1]; // Select Green Rainbow Color particles[i].b=colors[i%12][2]; // Select Blue Rainbow Color particles[i].xi=((rand()%50)-26.0f)*10.0f; // Random Speed On X Axis particles[i].yi=((rand()%50)-25.0f)*10.0f; // Random Speed On Y Axis particles[i].zi=((rand()%50)-25.0f)*10.0f; // Random Speed On Z Axis particles[i].xg=0.0f; // Set Horizontal Pull To Zero particles[i].yg=-0.8f; // Set Vertical Pull Downward particles[i].zg=0.0f; } } public void destroy() { //Destroy the GLContext cvsDispose(); } public void reshape(int width, int height) { //Reset The Current Viewport And Perspective Transformation gl.glViewport(0, 0, width, height); //Select The Projection Matrix gl.glMatrixMode(GL_PROJECTION); //Reset The Projection Matrix gl.glLoadIdentity(); //Calculate The Aspect Ratio Of The Window glu.gluPerspective(45.0f, getSize().width/(float)getSize().height, 0.1f, 200.0f); //Select The Modelview Matrix gl.glMatrixMode(GL_MODELVIEW); } public void display() { //Ensure GL is initialised correctly if (glj.gljMakeCurrent(true) == false) return; //Clear The Screen And The Depth Buffer gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Reset The View gl.glLoadIdentity(); //Select texture gl.glBindTexture(GL_TEXTURE_2D, texture[0]); for (int i=0; i < MAX_PARTICLES; i++) { // i Through All The Particles if (particles[i].active) { // If The Particle Is Active float x=particles[i].x; // Grab Our Particle X Position float y=particles[i].y; // Grab Our Particle Y Position float z=particles[i].z+zoom; // Particle Z Pos + Zoom // Draw The Particle Using Our RGB Values, Fade The Particle Based On It's Life gl.glColor4f(particles[i].r,particles[i].g,particles[i].b,particles[i].life); gl.glBegin(GL_TRIANGLE_STRIP); // Build Quad From A Triangle Strip gl.glTexCoord2d(1,1); gl.glVertex3f(x+0.5f,y+0.5f,z); // Top Right gl.glTexCoord2d(0,1); gl.glVertex3f(x-0.5f,y+0.5f,z); // Top Left gl.glTexCoord2d(1,0); gl.glVertex3f(x+0.5f,y-0.5f,z); // Bottom Right gl.glTexCoord2d(0,0); gl.glVertex3f(x-0.5f,y-0.5f,z); // Bottom Left gl.glEnd(); particles[i].x+=particles[i].xi/(slowdown*1000); // Move On The X Axis By X Speed particles[i].y+=particles[i].yi/(slowdown*1000); // Move On The Y Axis By Y Speed particles[i].z+=particles[i].zi/(slowdown*1000); // Move On The Z Axis By Z Speed particles[i].xi+=particles[i].xg; // Take Pull On X Axis Into Account particles[i].yi+=particles[i].yg; // Take Pull On Y Axis Into Account particles[i].zi+=particles[i].zg; // Take Pull On Z Axis Into Account particles[i].life-=particles[i].fade; // Reduce Particles Life By 'Fade' if (particles[i].life < 0.0f) { // If Particle Is Burned Out particles[i].life=1.0f; // Give It New Life particles[i].fade=rand()%100/1000.0f+0.003f; // Random Fade Value particles[i].x=0.0f; // Center On X Axis particles[i].y=0.0f; // Center On Y Axis particles[i].z=0.0f; // Center On Z Axis particles[i].xi=xspeed+((rand()%60)-32.0f); // X Axis Speed And Direction particles[i].yi=yspeed+((rand()%60)-30.0f); // Y Axis Speed And Direction particles[i].zi=((rand()%60)-30.0f); // Z Axis Speed And Direction particles[i].r=colors[col][0]; // Select Red From Color Table particles[i].g=colors[col][1]; // Select Green From Color Table particles[i].b=colors[col][2]; // Select Blue From Color Table } } } if (rainbow && delay > 25) { delay=0; col++; if (col > 11) col=0; // If Color Is To High Reset It } delay++; //Swap buffers glj.gljSwap(); glj.gljFree(); } public void keyTyped(KeyEvent e) { //We are not handling any keyboard events yet } public void keyPressed(KeyEvent e) { switch(e.getKeyCode()) { //Switch ON/OFF light when L is pressed // If Number Pad 8 And Y Gravity Is Less Than 1.5 Increase Pull Upwards case KeyEvent.VK_UP: for (int i = 0; i < MAX_PARTICLES; i++) { if (particles[i].yg < 1.5f) particles[i].yg+=0.01f; } break; // If Number Pad 2 And Y Gravity Is Greater Than 1.5 Increase Pull Downwards case KeyEvent.VK_DOWN: for (int i = 0; i < MAX_PARTICLES; i++) { if (particles[i].yg > 1.5f) particles[i].yg-=0.01f; } break; // If Number Pad 6 And X Gravity Is Less Than 1.5 Increase Pull Right case KeyEvent.VK_RIGHT: for (int i = 0; i < MAX_PARTICLES; i++) { if (particles[i].xg < 1.5f) particles[i].xg+=0.01f; } break; // If Number Pad 4 And X Gravity Is Greater Than 1.5 Increase Pull Left case KeyEvent.VK_LEFT: for (int i = 0; i < MAX_PARTICLES; i++) { if (particles[i].xg > 1.5f) particles[i].xg-=0.01f; } break; case KeyEvent.VK_TAB: // Tab Key Causes A Burst for (int i = 0; i < MAX_PARTICLES; i++) { particles[i].x=0.0f; // Center On X Axis particles[i].y=0.0f; // Center On Y Axis particles[i].z=0.0f; // Center On Z Axis particles[i].xi=((rand()%50)-26.0f)*10.0f; // Random Speed On X Axis particles[i].yi=((rand()%50)-25.0f)*10.0f; // Random Speed On Y Axis particles[i].zi=((rand()%50)-25.0f)*10.0f; // Random Speed On Z Axis } break; case KeyEvent.VK_ADD: for (int i = 0; i < MAX_PARTICLES; i++) { if (slowdown > 1.0f) slowdown-=0.01f; // Speed Up Particles } break; case KeyEvent.VK_SUBTRACT: for (int i = 0; i < MAX_PARTICLES; i++) { if (slowdown < 1.0f) slowdown+=0.01f; // Slow Up Particles } break; case KeyEvent.VK_PAGE_UP: zoom+=1.0f; // Zoom in break; case KeyEvent.VK_PAGE_DOWN: zoom-=1.0f; // Zoom out break; case KeyEvent.VK_ENTER: if (!rp) { rp = true; rainbow = !rainbow; } break; case KeyEvent.VK_SPACE: rainbow = false; sp=true; // Set Flag Telling Us Space Is Pressed delay=0; // Reset The Rainbow Color Cycling Delay col++; // Change The Particle Color if (col > 11) col=0; // If Color Is To High Reset It break; } } public void keyReleased(KeyEvent e) { switch(e.getKeyCode()) { //Key has been released case KeyEvent.VK_SPACE: sp = false; break; //Key has been released case KeyEvent.VK_ENTER: rp = false; break; } } } public class Particle { boolean active; // Active (Yes/No) float life; // Particle Life float fade; // Fade Speed float r; // Red Value float g; // Green Value float b; // Blue Value float x; // X Position float y; // Y Position float z; // Z Position float xi; // X Direction float yi; // Y Direction float zi; // Z Direction float xg; // X Gravity float yg; // Y Gravity float zg; // Z Gravity public Particle() { } } }