deleting light ?

Started by Veko, January 25, 2009, 03:22:35 PM

Previous topic - Next topic

Veko

Hi, I've got an Entity where I create a new Light along with some other stuff
This Entity has a limited life, so when the Entity reaches his max lifetime the Entity gets deleted (along with all the crap in it)

I haven't found a way to delete a light source from the world.
I know you can disable a light, but that doesn't work seeing I would receive a null pointer to the Entity's Light the next tick

I've been trying to find the Lights Object id so I could use the removeObject function, but lights don't seem to have that function ?

Can it be done with world.removeObject(...), or is there another way I'm missing to remove a Light.

greetz,
Veko

EgonOlsen

#1
No, lights don't have a remove()-method nor has the world. That's caused by the way they are implemented (which sucks btw...). Each light is identified by an ID when added to the world. That ID actually is the position in an array. If you delete a light in between, all other IDs wouldn't be valid any longer. That the reason why you can't delete a light. Maybe i should rework this part, but for now, a kind of light pool might be a better solution. A simple pool that stores a number of disabled lights and returns an unused one if the application requests one. If the application is done with that light, it will be returned to the pool.

What do you mean by that null pointer-thing? That disabling a light source doesn't work? That would be a bug in jPCT.

BTW: When working with lights, i strongly suggest to use the Light-class instead of the methods in World. They both do the same thing, but the Light-class hides at least some parts of the sucking implementation.

Veko

#2
Yes I know about the Light class here's what I'm doing

    public Entity
    {
        ... blah
        // Create a extra light source for the bullet:
        tracer = new Light(m_World);
        Util.setColor(tracer,new Color(255, 210, 0, 5));
        tracer.setPosition(start_pos);
    }

    protected void finalize() throws Throwable
    {
        ...
        m_World.removeObject(model);
        tracer.disable();
    }


The Light position gets updated with the transformedCenter of the model.

That all works but when I delete my Entity I get a null pointer when the tick tries to find the tracer object but can't because the Entity doesn't exist anymore,
That's why I thought it would be logical that I could delete my Light object like deleting an Object3D from the world (disabling a Light works fine, as long as I don't delete the entire Entity :p)


What goes on behind the scenes when adding a Object3D's to a world ? You could use a similiar structure for the Light objects, no ?

EgonOlsen

Quote from: Veko on January 25, 2009, 06:51:34 PM
What goes on behind the scenes when adding a Object3D's to a world ? You could use a similiar structure for the Light objects, no ?
It gets added to an internal list of Object3Ds. The finalize may happen at any time, i.e. it may happen during a render call. I think that this causes the null pointer, because removing an object from then world while rendering it isn't a good idea. Make sure that both operations don't interfere and the problem should be gone.

Of course it would be possible to use a similar structure for the lights. That would be a more clean and intuitive approach. However, that's not the way it was done in the past... :( I never was really happy with the lights but i never changed it...now i'm not going to change it anymore, because it would break all existing code. The introduction of Light was a compromise to hide some of the drawbacks of the actual implementation. Just write yourself a little LightPool-class as described above. It should only be a few minutes of work and it will hide the fact that you actually can't remove lights from the world.

Your code will look somehow like this then:

    public Entity
    {
        ... blah
        // Create a extra light source for the bullet:
        tracer = LightPool.getInstance().getLight();
        Util.setColor(tracer,new Color(255, 210, 0, 5));
        tracer.setPosition(start_pos);
    }

    protected void finalize() throws Throwable
    {
        ...
        m_World.removeObject(model);
        LightPool.getInstance().returnLightToPool(tracer);
    }



paulscode

I just so happened to need a LightPool class like this in a project I am currently working on, so I thought I would share the code for anyone who is interested.  It is pretty simple, but it might save someone a few minutes of typing  ;)

import com.threed.jpct.util.Light;
import com.threed.jpct.World;
import java.util.LinkedList;

/*
* The LightPool class provides an interface for recycling Light instances,
* since a Light can not be removed from a World once it has been created.
*/
public class LightPool
{
    /*
     * Handle to the World where the Light instances are to be used.
     */
    private World world;
    /*
     * List of light instances that can be reused.
     */
    private LinkedList<Light> recycledLights;
   
    /*
     * Constructor: instanciates the recycled lights list, and saves a handle
     * to the World.
     */
    public LightPool( World w )
    {
        world = w;
        recycledLights = new LinkedList<Light>();
    }
   
    /*
     * Removes a recycled Light from the list, enables it, and returns a handle
     * to it.
     * *OR*
     * If there are no more recycled lights in the list, creates a
     * new Light and returns a handle to it.
     */
    public Light getLight()
    {
        if( recycledLights.isEmpty() )
        {
            return new Light( world );
        }
        else
        {
            Light l = recycledLights.remove( 0 );
            l.enable();
            return l;
        }
    }
   
    /*
     * Disables the specified Light and places it into the list of recycled
     * Lights.
     */
    public void recycle( Light l )
    {
        if( l == null )
            return;
       
        l.disable();
       
        if( !recycledLights.contains( l ) )
        {
            recycledLights.add( l );
        }
    }
   
    /*
     * Clears the list of recycled Lights and removes handles to all
     * instanciated objects.
     * This method should be called when you are finished with a LightPool
     * instance.
     */
    public void cleanup()
    {
        recycledLights.clear();
        world = null;
        recycledLights = null;
    }
}


Basically, you can create a LightPool instance for each World, and simply call the getLight() method to get a light (recycles an old Light if possible, or creates a new one).  When you are done with a light, call the recycle( Light ) method.  The cleanup() method should be called when you are finished with the LightPool instance (at the end of your program for example).

zammbi

Just what I needed.

A suggestion for EgonOlsen:

Why not use the light pool internally?
So you can add a 'remove light' method but it disables the light and puts it on the pool.

This doesn't solve all problems but one less thing for people to worry about.

zammbi

Could you add a reset light to default settings method Egon?