# LWJGL Forum

## Programming => OpenGL => Topic started by: penguin on April 06, 2011, 13:55:30

Title: Problems with Particles
Post by: penguin on April 06, 2011, 13:55:30
I've played a bit with particles and wrote my own particle system.
I had it working and changed somethings and now it won't work anymore.

I've searched a long time for the wrong code, but i didn't find anything.

Here's my code:

ParticleEmitter.java
Code: [Select]
`package com.iceengine.particles;/** * * @author penguin */import org.lwjgl.util.vector.Vector3f;import org.lwjgl.util.Color;import java.util.Random;import org.lwjgl.opengl.GL11;import com.iceengine.j3d.objects.IObject;public class ParticleEmitter implements IObject{    private Vector3f position;    private Vector3f Rotation;    private int particleLifetime;    private Color myColor;    private Particle[] Particles;    private float size;    private Vector3f moveTo;    private boolean GenerateLifetime;    private boolean repeat;    public void drawObject(Vector3f theTranslation, Vector3f rotation)    {        GL11.glTranslatef(theTranslation.x, theTranslation.y, theTranslation.z);        GL11.glRotatef(rotation.x + Rotation.x, 1.0f, 0.0f, 0.0f);        GL11.glRotatef(rotation.y + Rotation.y, 0.0f, 1.0f, 0.0f);        GL11.glRotatef(rotation.z + Rotation.z, 0.0f, 0.0f, 1.0f);        Update();        drawParticles();    }    public void rotateX(float angle)    {        Rotation.x = angle;    }    public void rotateY(float angle)    {        Rotation.y = angle;    }    public void rotateZ(float angle)    {        Rotation.z = angle;    }    public ParticleEmitter(Vector3f Position,Vector3f MoveTo, Color color, int lifetime, int particles, float Size, boolean generateLifetime, boolean Repeat)    {        repeat = Repeat;        GenerateLifetime = generateLifetime;        moveTo = MoveTo;        size = Size;        position = Position;        myColor = color;        particleLifetime = lifetime;        Rotation = new Vector3f(0,0,0);        Particles = new Particle[particles];        Random generator = new Random();        for(int i = 0; i < particles; i++)        {            Particles[i] = createParticle();        }    }    public void Update()    {        for(int i = 0; i < Particles.length; i++)        {            Particles[i].Update();            if(!Particles[i].isParticleAlive())            {                if(repeat)                Particles[i] = createParticle();            }        }    }    private Particle createParticle()    {        float V = (float)(Math.random() * 25);        float angle = (float)(Math.random() * 360);        Vector3f velocity = new Vector3f();        velocity.x = (float)Math.sin(angle) * V;// * moveTo.x;        velocity.y = (float)Math.cos(angle) * V;// * moveTo.y;        velocity.z = (((float)Math.random() * 10) - 5) / 10 * V;// * moveTo.z;//generator.nextFloat() % 10)-5) / 10 * V;        //System.out.println("NEW " + " " + position.x + " " + position.y + " " + position.z + " " + angle + " " + V + " " + velocity.x + " " + velocity.y + " " + velocity.z);        int life = 0;        if(GenerateLifetime)        {            life = (int)(Math.random() * particleLifetime);        } else {            life = particleLifetime;        }        return new Particle(size,life, new Vector3f(position.x, position.y, position.z),velocity, myColor);    }    public void drawParticles()    {        for(int i = 0; i < Particles.length; i++)        {            //System.out.println("DRAW " + i + " - " + Particles[i].position.x + " - " + Particles[i].position.y + " - " + Particles[i].position.z + " - " + Particles[i].velocity.x + " - " + Particles[i].velocity.y + " - " + Particles[i].velocity.z + " - " + (System.currentTimeMillis() - Particles[i].startTime));            Particles[i].drawParticle();        }    }    public boolean isWorkDone()    {        boolean workDone = false;        for(int i = 0; i < Particles.length; i++)        {            if(!Particles[i].isParticleAlive())                workDone = true;        }        return workDone;    }}`
Particle.java
Code: [Select]
`package com.iceengine.particles;/** * * @author penguin */import org.lwjgl.util.vector.Vector3f;import org.lwjgl.util.Color;import org.lwjgl.opengl.GL11;public class Particle {    public Vector3f position;    private int lifetime;    public Vector3f velocity;    private Color myColor;    public long startTime;    private boolean alive;    private float size;    public Particle(float Size,int Lifetime, Vector3f Position, Vector3f Velocity, Color color)    {        size = Size;        position = Position;        lifetime = Lifetime;        myColor = color;        velocity = Velocity;        startTime = System.currentTimeMillis();        alive = true;    }    public void Update()    {        // Update Particle        if(alive)        {            if((System.currentTimeMillis() - startTime) <= lifetime)            {                position.x += velocity.x / 250;                position.y += velocity.y / 250;                position.z += velocity.z / 250;                velocity.x *= 0.975f;                velocity.y *= 0.975f;                velocity.z *= 0.975f;            } else {                alive = false;            }        }    }    public void drawParticle()    {        /*        GL11.glBegin(GL11.GL_TRIANGLE_STRIP);        GL11.glColor3f(myColor.getRed(), myColor.getGreen(), myColor.getBlue());        position.z = - 2.0f;        GL11.glVertex3f(position.x+size, position.y+size, position.z);        GL11.glVertex3f(position.x-size, position.y+size, position.z);        GL11.glVertex3f(position.x+size, position.y-size, position.z);        GL11.glVertex3f(position.x-size, position.y-size, position.z);        GL11.glEnd();        */                if (alive)        {            GL11.glBegin(GL11.GL_POINT);                GL11.glColor3f(myColor.getRed(), myColor.getGreen(), myColor.getBlue());                GL11.glVertex3f(position.x, position.y, position.z);                System.out.println("drawParticle() - " + position);            GL11.glEnd();        }             }    public boolean isParticleAlive()    {        return alive;    }}`
Here's how i use the code:
Code: [Select]
`          ParticleEmitter theEmitter = new ParticleEmitter(new Vector3f(0,0,0), new Vector3f(1,1,0), new org.lwjgl.util.Color(org.lwjgl.util.Color.WHITE), 4000, 100, 1.0f, true, true);                    while(!IceEngine.isEngineCloseRequested()) // Same as Display.isCloseRequested();          {              org.lwjgl.opengl.GL11.glClear(org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT | org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT);              org.lwjgl.opengl.GL11.glLoadIdentity();              theEmitter.Update();              theEmitter.drawParticles();                            IceEngine.update(); // Same as Display.update();          }`
I hope someone can help me :)
Title: Re: Problems with Particles
Post by: CodeBunny on April 06, 2011, 15:52:58
Where do you screw up? Is it a hard crash, or does it not render appropriately, or is it an update bug?

Additionally, are you sure you want to use an array for your particles? You can't get dynamic numbers of them that way. Something like a LinkedList - mostly O(1) complexity for adding and clearing - is probably a good way to go (although it's O(n) removal time is a drag).
Title: Re: Problems with Particles
Post by: penguin on April 06, 2011, 16:03:52
It just don't render anything.

Yes, i know that an Array is not a good method to store particles.
But this is just a really really first "test-code" because i'm new to opengl.
Title: Re: Problems with Particles
Post by: avm1979 on April 06, 2011, 17:40:04
Hard to say for sure without seeing all your code, but you should make sure to:
- disable lighting
- disable textures
- enable blending and set the right blend mode

Something like:
glDisable(GL_LIGHTING);
glDisable(GK_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

If lighting is on and you don't have lights set up, well, everything will be very dark :)
If textures are on but you haven't bound one, I believe it'll pick up black for the texture color, and again, things will be black.

Something you can try to spot these more easily is set the clear color to something other than black - that way, you'll see black shapes against it if you mess up with lighting/textures.

It's also possible there's a problem with your view setup - i.e., you could be rendering just fine, but not looking at it.
Title: Re: Problems with Particles
Post by: penguin on April 06, 2011, 19:14:34
Textures are enabled, but my particle system worked already with it, but how i've said after some code changes it wouldn't work well now :S

My view setup is okay, tested with a little triangle at 0,0,-1.
Blending is currently disabled, i will use it later when i code the fade out for particles.
Lightning is disabled, too.
Particle Color is set to White.

Thanks for your help, but there must be another problem and i damn don't find it :S
Title: Re: Problems with Particles
Post by: avm1979 on April 06, 2011, 20:22:02
If textures are enabled, that's a problem.  Try disabling GL_TEXTURE_2D before rendering the particles and re-enabling after. That's the way you ought to be doing it anyway, unless your particles are actually using textures (which it doesn't look like).

It may have worked before because the last texture coord you set happened to point to a white texel, so it looked the way you'd expect, but for the wrong reason :)
Title: Re: Problems with Particles
Post by: penguin on April 06, 2011, 20:40:15
I've tried, but still nothing appears  :-\
Title: Re: Problems with Particles
Post by: jediTofu on April 07, 2011, 00:46:02

Draw your particles, but comment out update particles method line (or have a boolean switch and just run update particles method once).  This way you may get closer in determining if it is either an OpenGL problem (not drawing correctly) or a particle problem (lifetime is too small, etc.).  Try just figuring out how to draw one particle in the center of the screen in a new class.

Ctrl+Z (undo) is your friend (most IDEs have a huge buffer size for this; I went back 2 hours of work before) or some type of history storage versioning system or just create a new class with copy and paste for testing purposes.

If you want, I can post my code for a simple particle class I wrote a while back just for testing.
Title: Re: Problems with Particles
Post by: penguin on April 07, 2011, 04:33:02
Now i've tried with update my Particles only one time, but still i see there a black screen.
I've also debugged lifetime, positions, movement and color.
Everything looks okay, but still they don't get drawn.

If it's okay, you can post your code. Maybe it will be helpful :)
Title: Re: Problems with Particles
Post by: CodeBunny on April 07, 2011, 13:48:34
You're able to draw other stuff, right? Like textured quads and so forth?

Also:

In your render particle method, you set the particle's z position to -2.0f. Is this intended? This looks like a 3D particle system, so I'm a little confused as to why you constain the z parameter.

Oh, and your isWorkDone() method would be a bit more efficient like:

Code: [Select]
`    public boolean isWorkDone()    {        for(int i = 0; i < Particles.length; i++)        {            if(!Particles[i].isParticleAlive())                return true;        }        return false;    }`
This way it exits as soon as it knows it needs to return true, instead of going through the list every time. I'm a little confused as to what it does, though - it seems like it should return true when every particle is dead, but instead returns true as soon as a single one is. If that's the case, you should remove your exclamation mark.
Title: Re: Problems with Particles
Post by: penguin on April 07, 2011, 14:31:03
I've played a bit with GL_TEXTURES_2D Value and now the particles get drawn.

This is the new Draw code in the ParticleEmitter.
Code: [Select]
`    public void drawParticles()    {        GL11.glDisable(GL11.GL_TEXTURE_2D);        for(int i = 0; i < Particles.length; i++)        {            Particles[i].drawParticle();        }        GL11.glEnable(GL11.GL_TEXTURE_2D);    }`
But now my objects textures doesn't get mapped, every object is white.

I set Z to -2.0f just for testing purposes only :)
Title: Re: Problems with Particles
Post by: CodeBunny on April 07, 2011, 21:43:12
If that's the case, you most likely have a texture mapped but its coordinate was on a transparent pixel.

If you want to bind a texture you'll have to add the texture mapping commands into the particle draw code.
Title: Re: Problems with Particles
Post by: penguin on April 08, 2011, 10:03:44
No, I mean the Textures on other Objects, for now i won't add Textures to my particle system.

If i add draw an quad for example with texture but my particle system drawing method is not called i see the quad with the texture, but if i call the particle draw function the quad will be white and no texture is there at all.
Title: Re: Problems with Particles
Post by: CodeBunny on April 08, 2011, 11:05:57
Do you call texture.bind() or GL11.glBindTexture2D(....) after your particle system is called?
Title: Re: Problems with Particles
Post by: penguin on April 08, 2011, 11:10:50
I use the Slick-util library and call texture.bind() to bind textures.
Title: Re: Problems with Particles
Post by: Fool Running on April 08, 2011, 12:43:45
Do you have any setup on the points rendering? (i.e. Are you enabling GL_POINT_SMOOTH (round points instead of square) and/or GL_POINT_SPRITE_ARB (drawing points with generated texture coordinates)?)
If so, try disabling them. Certain combinations of settings for points rendering don't work correctly and result is the points not showing up.

EDIT:
The problem I remember was when you enable GL_POINT_SMOOTH and GL_POINT_SPRITE_ARB at the same time. The points don't show up (and will suffer performance-wise).
Title: Re: Problems with Particles
Post by: jediTofu on April 09, 2011, 01:27:47
This was made back when I was first learning LWJGL.  I tried to clean it up a bit.  It also had tons and tons of testing stuff I was doing that I had to strip out to just have particle stuff haha.  It's not very good, just very very simple particle system.  This may help you figure out the problem.  There's actually a pretty good particle system tutorial/demo somewhere using LWJGL; maybe the ninja cave tutorials?

I was just going to add it as an attachment...but these forums won't let you attach a .java file......oh, the irony

Code: [Select]
`import static org.lwjgl.opengl.GL11.*;import java.awt.BorderLayout;import java.awt.Dimension;import java.awt.Toolkit;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.IntBuffer;import java.util.HashMap;import java.util.Map;import java.util.LinkedList;import java.util.ListIterator;import javax.swing.JDialog;import javax.swing.JLabel;import javax.swing.JPanel;import javax.swing.JProgressBar;import org.lwjgl.BufferUtils;import org.lwjgl.LWJGLException;import org.lwjgl.input.Cursor;import org.lwjgl.input.Keyboard;import org.lwjgl.input.Mouse;import org.lwjgl.opengl.Display;import org.lwjgl.opengl.DisplayMode;import org.newdawn.slick.opengl.Texture;import org.newdawn.slick.opengl.TextureLoader;import org.newdawn.slick.util.Log;/** * @author jediTofu */public class Particles {  public static void main(String[] args) {    Particles p = null;    try {      p = new Particles();      while(!Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)        && !Display.isCloseRequested()) {        if(Display.isVisible()) {          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);          p.run();        }        else {          try { Thread.sleep(100); } catch(Exception e) {}        }        Display.update();        Display.sync(100);      }    }    catch(Exception ex) {      ex.printStackTrace();      System.exit(1300);    }    finally {      if(p != null) {        p.destroy();      }    }  }  //lwjgl  public static int HEIGHT = 768;//Display.getDesktopDisplayMode().getHeight();  public static int WIDTH = 1024;//Display.getDesktopDisplayMode().getWidth();  public static boolean IS_FULLSCREEN = false;  public static boolean MOUSE_IS_GRABBED = false;  //particles  public static int MAX_PARTICLES = 1000;  public static int PARTICLE_HEIGHT = 64;  public static int PARTICLE_WIDTH = 64;  public static float PARTICLE_X_FACTOR = 4.0f;  public static float PARTICLE_Y_FACTOR = 4.0f;  public LinkedList<Particle> particleList = new LinkedList<Particle>();  public Texture particleTexture = null;  public int particleX = 0;  public int particleY = 0;  public Particles() throws LWJGLException,FileNotFoundException,IOException {    //progress bar    int progress = 0;    JProgressBar progressBar = new JProgressBar(0,5);    progressBar.setPreferredSize(new Dimension(300,50));    progressBar.setStringPainted(true);    JLabel label = new JLabel("Loading...",JLabel.CENTER);    JPanel panel = new JPanel(new BorderLayout());    panel.add(progressBar,BorderLayout.PAGE_START);    panel.add(label,BorderLayout.CENTER);    JDialog dialog = new JDialog((JDialog)null,"Loading");    dialog.setAlwaysOnTop(true);    dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);    dialog.getContentPane().add(panel);    dialog.pack();    dialog.setLocationRelativeTo(null);    dialog.setVisible(true);    dialog.toFront();    //lwjgl    label.setText("Setting up LWJGL Display...");    Display.setDisplayMode(findDisplayMode(WIDTH,HEIGHT));    Display.setFullscreen(IS_FULLSCREEN);    Display.setTitle("Particles");    Display.create();    dialog.setLocationRelativeTo(null);    progressBar.setValue(++progress);    label.setText("Setting up LWJGL Keyboard...");    Keyboard.create();    progressBar.setValue(++progress);    label.setText("Setting up LWJGL Mouse...");    Mouse.create();    Mouse.setGrabbed(MOUSE_IS_GRABBED);    progressBar.setValue(++progress);    //opengl    label.setText("Setting up OpenGL...");    glEnable(GL_TEXTURE_2D); glShadeModel(GL_SMOOTH); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING);    glClearColor(0.0f,0.0f,0.0f,0.0f);    glClearDepth(1.0);    glAlphaFunc(GL_GREATER,0.1f) ;    glEnable(GL_ALPHA_TEST) ;    glEnable(GL_BLEND);    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);    glViewport(0,0,WIDTH,HEIGHT);    glMatrixMode(GL_MODELVIEW);    glMatrixMode(GL_PROJECTION);    glLoadIdentity();    glOrtho(0.0,WIDTH,0.0,HEIGHT,1.0,-1.0);    glMatrixMode(GL_MODELVIEW);    progressBar.setValue(++progress);    //texture    label.setText("Loading Textures...");    particleTexture = TextureLoader.getTexture("PNG",new FileInputStream("particle.png"));    progressBar.setValue(++progress);    dialog.dispose();  }  public void destroy() {    if(particleTexture != null) {      particleTexture.release();    }    Mouse.destroy();    Keyboard.destroy();    Display.destroy();  }  public void drawParticle(float x,float y,float red,float green,float blue,float alpha) {    x -= particleX + (PARTICLE_WIDTH / 2);    y -= particleY + (PARTICLE_HEIGHT / 2);    particleTexture.bind();    //glPushMatrix();    //glTranslatef(x,y,0);    glColor4f(red,green,blue,alpha);    glBegin(GL_QUADS);    glTexCoord2f(0,0);    glVertex2f(x,y);    glTexCoord2f(1,0);    glVertex2f(x + particleTexture.getTextureWidth(),y);    glTexCoord2f(1,1);    glVertex2f(x + particleTexture.getTextureWidth(),y + particleTexture.getTextureHeight());    glTexCoord2f(0,1);    glVertex2f(x,y + particleTexture.getTextureHeight());    glEnd();    //glPopMatrix();  }  public DisplayMode findDisplayMode(int width,int height) {    return(findDisplayMode(width,height,Display.getDesktopDisplayMode().getBitsPerPixel()));  }  public DisplayMode findDisplayMode(int width,int height,int bpp) {    try {      for(DisplayMode mode: Display.getAvailableDisplayModes()) {        if(mode.getBitsPerPixel() == bpp && mode.getHeight() == height          && mode.getWidth() == width) {          return(mode);        }      }    }    catch(LWJGLException ex) {    }    return(null);  }  public void run() {    //drawParticle(0,0,1.0f,0.0f,0.0f,1.0f);    for(ListIterator<Particle> it = particleList.listIterator(); it.hasNext();) {      Particle p = it.next();      if(p.isDead()) {        it.remove();        p = null;      }      else {        p.play();        p.age(1.0);      }    }    if(particleList.size() < MAX_PARTICLES      && (Mouse.getDX() != 0 || Mouse.getDY() != 0)) {      for(int i = 0; i < 40; i++) {        float red=0.0f,green=0.0f,blue=0.0f;        switch((int)(Math.random() * 4.0)) {          case 0: red   = (float)Math.random(); break;          case 1: green = (float)Math.random(); break;          case 2: blue  = (float)Math.random(); break;          case 3: red = green = blue = (float)Math.random(); break;        }        particleList.add(new Particle          ((float)Math.random() / 40.0f          ,Mouse.getX()          ,Mouse.getY()          ,(float)Math.random() + (float)(int)(Math.random() * PARTICLE_X_FACTOR) * ((int)(Math.random() * 2.0) == 0 ? 1 : -1)          ,(float)Math.random() + (float)(int)(Math.random() * PARTICLE_Y_FACTOR) * ((int)(Math.random() * 2.0) == 0 ? 1 : -1)          ,red,green,blue));      }    }  }  public class Particle {    private float blue;    private float green;    private float lifespan;    private float lifeVelocity;    private float red;    private float x;    private float xVelocity;    private float y;    private float yVelocity;    public Particle(float lifeVelocity,float x,float y,float xVelocity,float yVelocity,float red,float green,float blue) {      this.blue = blue;      this.green = green;      this.lifespan = 1.0f;      this.lifeVelocity = lifeVelocity;      this.red = red;      this.x = x;      this.xVelocity = xVelocity;      this.y = y;      this.yVelocity = yVelocity;    }    // delta is meant for smoother timing, but not using it right now up above    public void age(double delta) {      /*switch((int)(Math.random() * 2)) {        case 0: x -= xVelocity; break;        case 1: x += xVelocity; break;      }      switch((int)(Math.random() * 2)) {        case 0: y -= yVelocity; break;        case 1: y += yVelocity; break;      }*/      x += xVelocity * delta;      y += yVelocity * delta;      lifespan -= lifeVelocity * delta;      if(lifespan < 0.0f) {        lifespan = 0.0f;      }    }    public boolean isDead() {      return(lifespan <= 0.0f || x < (-PARTICLE_WIDTH) || x > WIDTH        || y < (-PARTICLE_HEIGHT) || y > HEIGHT);    }    public void play() {      /*glColor3f(red * lifespan,green * lifespan,blue * lifespan);      glBegin(GL_POINTS);      glVertex2f(x * lifespan,y / lifespan);      glVertex2f(x,y);      glEnd();*/      //drawParticle(x * lifespan,y / lifespan,red,green,blue,lifespan);      drawParticle(x,y,red,green,blue,lifespan);    }  }}`
Title: Re: Problems with Particles
Post by: penguin on April 09, 2011, 09:52:47
I've tried my things now, but my Object will still get drawn without a texture if the particle emitter is added to my "world".
If I don't add the emitter to my "world", my figure will get drawn with textures.

I've uploaded a version of my full code, because i think i've posted not enough code.

I hope someone can help me, I've worked on this for much hours now.

Edit://

Found the mistake, but don't know how to fix it.
When the particles colour is set, the model that should have a texture gets the same colour.
Title: Re: Problems with Particles
Post by: CodeBunny on April 10, 2011, 20:33:54
I don't really have time to go through your engine, but it sounds to me like you only bind your texture once.

If you disable and enable textures, I believe that the end result is that you don't have any texture bound - as if you had called GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0).

So, for each time you render your object, bind the texture again and see what happens.

I'm pretty sure this is your problem. In your initial particle code, you called:

Code: [Select]
`        GL11.glBegin(GL11.GL_TRIANGLE_STRIP);        GL11.glColor3f(myColor.getRed(), myColor.getGreen(), myColor.getBlue());        position.z = - 2.0f;        GL11.glVertex3f(position.x+size, position.y+size, position.z);        GL11.glVertex3f(position.x-size, position.y+size, position.z);        GL11.glVertex3f(position.x+size, position.y-size, position.z);        GL11.glVertex3f(position.x-size, position.y-size, position.z);        GL11.glEnd();`
There isn't any code to map each vertex to a texture coordinate. Thus, they all have the same coordinate and you get a monochromatic quad, and if that's a transparent spot, you obviously don't see anything.
Title: Re: Problems with Particles
Post by: penguin on April 10, 2011, 21:15:30
I have no problems with particles anymore, I patched everything and it now runs fine, but the other stuff won't work if my ParticleEmitter is added to the world.
This is because i set the Color for the Particles in each draw and if i draw another Object (Like an Model with UV-Mapping) it have the same color like my particles and there is no texture on them.

But if i don't add the ParticleEmitter to the World the Models get drawn fine, so i think i must clear some type of a colorbuffer in opengl.
Title: Re: Problems with Particles
Post by: CodeBunny on April 13, 2011, 12:26:10
When you draw your models, do you re-bind the texture every time you pass through the rendering code?