Particle generator for LWJGL

Like in topic. I made my own particle generator for 2D games in LWJGL. It seems to be very efficient, 50000 particles use about 80% of my CPU power. You can set many parameters and use your own particle texture. Textures for this generator should have alpha background and white color.

I am using this code to make engine particles in my space game, I think that effects are looking good.

Particle_Generator class:

package Game;

import de.matthiasmann.twl.utils.PNGDecoder;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.opengl.GL11;
import static org.lwjgl.opengl.GL11.*;

public class Particle_Generator {
    
    int particleCount = 0;
    int generationSpeed = 0;
    int generationNow = 0;
    Particle[] particles;
    
    private float x = 0;
    private float y = 0;
    private int life = 0;
    
    private float r = 0;
    private float g = 0;
    private float b = 0;
    private float a = 0;
    
    private boolean emitter = false;
    
    private float speed = 0;
    private float angle = 0;
    private float range = 0;
    
    ByteBuffer bufParticle;
    PNGDecoder decParticle;
    
    private class Particle {
        
        private Particle_Generator daddy = null;
        private float x = 0.0f;
        private float y = 0.0f;
        private float speed = 1.001f;

        private float r = 0;
        private float g = 0;
        private float b = 0;
        private float a = 1;

        private float personalAngle = 0;
        private float lifespan;
        private float angleDiff = 0;
        
        private boolean dead = false;
        
        public Particle(Particle_Generator generator) {
            daddy = generator;
            r = (float) (daddy.r + (Math.random()/10));
            g = (float) (daddy.g + (Math.random()/10));
            b = (float) (daddy.b + (Math.random()/10));
            a = (float) (daddy.a + (Math.random()/10));
            x = daddy.x;
            y = daddy.y;
            lifespan = daddy.life;
            speed = (float) (daddy.speed + (Math.random()*daddy.speed/10));
            personalAngle = daddy.angle;
                
            float rangecalc = (float) Math.random();
            boolean isAboveZero = false;
            if(rangecalc>.5) {isAboveZero = true;}
            rangecalc = (float) Math.random();
            if (isAboveZero==true) {angleDiff = daddy.range*rangecalc;}
            else {angleDiff = -range*rangecalc;}
            personalAngle = personalAngle + angleDiff;   
        }
        
        private void update() {
            if(Math.random()>.5){
                    x += 1*Math.random();
            }
            else{
                    x -= 1*Math.random();
            }
            if(Math.random()>.5){
                    y += 1*Math.random();
            }
            else{
                    y -= 1*Math.random();
            }
            
            x -= Math.sin(personalAngle) * speed;
            y += Math.cos(personalAngle) * speed;
            
            draw();
            weaken();
        }
        
        private void draw() {
            glColor4d(r,g,b,a);
            glTexCoord2d(1,0); glVertex2d(-10, -10);
            glTexCoord2d(1,1); glVertex2d(-10, 10);
            glTexCoord2d(0,1); glVertex2d(10, 10);
            glTexCoord2d(0,0); glVertex2d(10, -10);
        }

        private void move(float x, float y){
            this.x=x;
            this.y=y;
        }
        
        private void weaken(){
            lifespan--;
            if (lifespan<0) {dead=true;}
        }

    }
    
    public Particle_Generator(int size, int generate, int life, float r, float g, float b, float a, boolean emit, float x, float y, float speed, float angle, int range) {
        if(size<=0) {
            return;
        }
        particleCount = size;
        generationSpeed = generate;
        this.r = r;
        this.g = g;
        this.b = b;
        this.a = a;
        this.x = x;
        this.y = y;
        this.life = life;
        this.speed = speed;
        this.angle = angle;
        this.range = (float) (range*Math.PI/180);
        emitter = emit;
        generate();
        particleTexture();
    }

    private void generate() {
        particles = new Particle[particleCount];
        for(int i = 0; i < particleCount; i++) {particles[i] = new Particle(this);}
        for(int i = 0; i < particleCount; i++) {particles[i].lifespan = 1;}
    }
    
    public void update(boolean produce) {
        for(int i = 0; i < particleCount; i++) {
            if(particles[i] != null){
                if(particles[i].dead==true){
                    particles[i] = null;
                }
                else {particles[i].update();}
            }
            else{
                if (produce==true) {
                particles[i] = null;
                if(emitter){
                    if (generationSpeed>generationNow) {
                    particles[i] = new Particle(this);
                    generationNow++;
                    }
                }
                }
        }
        }
        generationNow = 0;
    }

    public void draw() {
                for(int i = 0; i < particleCount; i++) {
                        if(particles[i] != null){
                            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, decParticle.getWidth(), decParticle.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, bufParticle);
                            glBindTexture(GL_ADD, GL_LOAD);
                            glBindTexture(GL_TEXTURE_2D,1);
                            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                            glPushMatrix();
                            glTranslated(particles[i].x+Core.SCREEN_X/2, particles[i].y+Core.SCREEN_Y/2, 0);
                            glBegin(GL_QUADS);
                                particles[i].draw();
                            glEnd();
                            glPopMatrix();
                        }
                }
        }
        
        public void move(float x, float y){
                this.x = x;
                this.y = y;
        }
        public void killme(){
                for(int i = 0; i < particleCount; i++) {
                    if (particles[i]!=null) {
                        particles[i] = null;
                }
            }
        }
        
        public void setspeed(float Speed){
                this.speed = Speed;
        }
        public void setAngle(float Angle){
                this.angle=Angle;
        }
        public void setRange(float Range){
                this.range=(float) (Range*Math.PI/180);
        }

public void particleTexture() {
        InputStream in = null;
        try {
            in = new FileInputStream(YOUR_PARTICLE_TEXTURE);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Models.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
        decParticle = new PNGDecoder(in);
        bufParticle = ByteBuffer.allocateDirect(4*decParticle.getWidth()*decParticle.getHeight());
        decParticle.decode(bufParticle, decParticle.getWidth()*4, PNGDecoder.Format.RGBA);
        bufParticle.flip();
        } catch (IOException ex) {
            Logger.getLogger(Models.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            in.close();
        } catch (IOException ex) {
            Logger.getLogger(Models.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
}

Use this code to create new generator:

Particle_Generator generator;
generator = new Particle_Generator(MAX_NUMBER_OF_PARTICLES, PARTICLES_PER_UPDATE, LIFE_LENGTH, COLOR_R, COLOR_G, COLOR_B, ALPHA, START_AT_CREATION, POSITION_X, POSITION_Y, PARTICLES_SPEED, ANGLE, RANGE);

Update particles logic and life time:

generator.update(true); //update existing particles and generate new ones
generator.update(false); //update existing particles, but do not generate new ones

Update particles graphics:

generator.draw();

What do you think about this code?

I was just looking around to learn about particle generating and I stumbled across your generator. Thought I’d try it.

It was not complicated to get it running. It has a sideeffect on my engine in a way which I can’t figure out but that’s not a problem now. I made a little testing texture and recorded a video with it running:

kgp7pi4eBuM

EDIT: lol, there is a song playing in the background which I listened to while recording… who knows the title? g

I don’t know if the stuttering is normal but it seems a bit strange.

I missed a little example for the parameters the constructor needed.
However, I will now go on learning more about this stuff and eventually try to code my own generator. :stuck_out_tongue:

[]glTexImage2D “uploads” the texture data to the GPU and incurs a pipeline stall. Doing this every frame, let alone every particle can dramatically reduce your frame rate.
[
]glBindTexture(GL_ADD, GL_LOAD); is invalid GL code
[]Instead of always binding texture unit 1, you should create your own texture wrapper class so that you can (a) reuse it across your entire project and (b) use multiple texture sheets. Read the basics on textures here.
[
]You don’t need to push/pop a new matrix onto the stack for each particle. You also don’t need to use glTranslate (which can be pretty slow), instead it would be better to just adjust the position before calling glVertex2f
[]Don’t call glBegin/glEnd for each particle; call glBegin once before the loop, and glEnd after the loop
[
]If you want better performance you need to drop immediate mode (which is deprecated) and pick up VBOs. Even plain old vertex arrays will perform faster.

I won’t comment on the rest of your code… :stuck_out_tongue: