Problems with Particles

Started by penguin, April 06, 2011, 13:55:30

Previous topic - Next topic

penguin

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
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
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:
          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 :)

CodeBunny

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).

penguin

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.

avm1979

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.

penguin

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

avm1979

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 :)

penguin

I've tried, but still nothing appears  :-\

jediTofu

Helpful debug lesson:

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.



Helpful IDE tip:

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.
cool story, bro

penguin

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 :)

CodeBunny

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:

    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.

penguin

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.
    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 :)

CodeBunny

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.

penguin

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.

CodeBunny

Do you call texture.bind() or GL11.glBindTexture2D(....) after your particle system is called?

penguin

I use the Slick-util library and call texture.bind() to bind textures.