[commited] SimpleLightNode / LightStateController serialization bugs

Owaye has reported two bugs in the serialization of  SimpleLightNode and LightStateController.



The following patches are for jme1 but the problem also exists in jme2:



see http://www.jmonkeyengine.com/jmeforum/index.php?topic=9390.0

and Issue #19



Problem 1:

The LightStateController isn’t saving the reference to the LightManagement. Also its accessing the LightManagement through the Singleton instead of the reference.

As a result, after loading the serialized scene, the lightList in LightManagement is lost and thus all LightStates will be disabled.

The Constructor of LightStateController already takes the LightManagement as a parameter but its not used yet.

If we keep a reference to LightManagement and save it together with LightStateController it will behave correctly.


edit:

LightStateController / LightManagement is not needed anymore in jme 2. no fix needed,





Problem 2 [already commited]:

loading  / saving lights in the SimpleLightNode can’t work because of a typo:

        capsule.write(light, "Light", null);
vs.
        light = (Light)capsule.readSavable("light", null);




A TestCase to verify the problem:
Pressing 'X' saves the scene, detaches the old scene from the root Node and attaches the newly loaded scene again.
(jme1 source)


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import javax.swing.JFileChooser;
import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingSphere;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.light.LightControllerManager;
import com.jme.light.PointLight;
import com.jme.light.SimpleLightNode;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.LightState;
import com.jme.util.export.binary.BinaryExporter;
import com.jme.util.export.binary.BinaryImporter;
import com.jme.util.export.binary.BinaryOutputCapsule;

public class TestSaveLightStateController extends SimpleGame {
    final static float worldsize = 7;//The size of the world
    Node colornode;
    JFileChooser fileChooser;
    InputHandler input;
    int loadedPosition = 0;
    ByteArrayOutputStream out = new ByteArrayOutputStream();
   
    public static void main(String[] args) {
        TestSaveLightStateController app = new TestSaveLightStateController();
        app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }
   
    void randomLight(int i) {
        //Chose the color for the lights.
        ColorRGBA LightColor = ColorRGBA.randomColor();

        //Create a sphere to show where the light is in the demo.
        Sphere LightSphere = new Sphere("lp" + i, 10, 10, .1f);
        LightSphere.setModelBound(new BoundingSphere());
        LightSphere.updateModelBound();
        LightSphere.setLightCombineMode(com.jme.scene.state.LightState.OFF);
        LightSphere.setDefaultColor(LightColor);

        //Create a new point light and fill out the properties
        PointLight pointLight = new PointLight();
        pointLight.setAttenuate(true);
        pointLight.setConstant(.1f);
        pointLight.setLinear(.1f);
        pointLight.setQuadratic(.1f);
        pointLight.setEnabled(true);
        pointLight.setDiffuse(LightColor);
        pointLight.setAmbient(new com.jme.renderer.ColorRGBA(.1f, .1f, .1f, .1f));

        //Add the light to the world part 1:
        //Add the light to the state creator.
        LightControllerManager.addLight(pointLight);

        //Create a node to hold the light and add a light node with this light.
        Node mnod = new Node("P" + i + " Light pos");
        SimpleLightNode ln = new SimpleLightNode("ln" + i, pointLight);
        mnod.setLocalTranslation(new Vector3f(FastMath.rand.nextFloat()
                * worldsize * 2 - worldsize, FastMath.rand.nextFloat()
                * worldsize * 2 - worldsize, FastMath.rand.nextFloat()
                * worldsize * 2 - worldsize));
        mnod.attachChild(LightSphere);
        mnod.attachChild(ln);
       
        colornode.attachChild(mnod);
    }

    void randomSphere(int i) {
        //Crate a sphere and position it.
        Sphere newSphere = new Sphere("sp" + i, 10, 10, 1);
        newSphere.setModelBound(new BoundingSphere());
        newSphere.updateModelBound();
        newSphere.setLocalTranslation(new Vector3f(FastMath.rand.nextFloat()
                * worldsize * 2 - worldsize, FastMath.rand.nextFloat()
                * worldsize * 2 - worldsize, FastMath.rand.nextFloat()
                * worldsize * 2 - worldsize));
        //Add a new state to the controller. We do not use createLightState
        // because it would require that the world bounds be updated first.
        LightState ls = com.jme.system.DisplaySystem.getDisplaySystem().getRenderer().createLightState();
        ls.setEnabled(true);
        newSphere.setRenderState(ls);

        //Create a controller to update the lighting and set the combine modes
        // to REPLACE. !!All other combine modes will not work!!
        LightControllerManager.addSpatial(newSphere);
        newSphere.setLightCombineMode(LightState.REPLACE);
       
        colornode.attachChild(newSphere);
    }

    public void simpleInitGame() {
        //First we remove all the lights from the lightState
        this.lightState.detachAll();

        // create colornode
        colornode = new Node();
       
        for (int i = 0; i < 20; i++) {
            this.randomLight(i);
        }
        //Add the spheres.
        for (int i = 0; i < 10; i++) {
            this.randomSphere(i);
        }
       
        rootNode.attachChild(colornode);
        rootNode.updateRenderState();
       
        input = new InputHandler();       
        InputAction keyAction = new InputAction() {
           public void performAction( InputActionEvent iae){
              if(iae.getTriggerPressed()) {
                  if(iae.getTriggerIndex()==KeyInput.KEY_X) {
                     save();
                     load();
                  }
               }
            }
        };
        input.addAction(keyAction,InputHandler.DEVICE_ALL,InputHandler.BUTTON_ALL,InputHandler.AXIS_NONE,false);
//        fileChooser = new JFileChooser();
    }
   
    public void save() {
      /*** Write File ***/      
      try {
         BinaryExporter.getInstance().save(colornode, out);
      } catch (Exception e) {
         e.printStackTrace();
      }
    }
   
    public void load() {
       rootNode.detachAllChildren();
       lightState.detachAll();
       
      /*** Read File ***/
      try   {
         //Select the file you want to load
         Node loaded = (Node) BinaryImporter.getInstance().load(new ByteArrayInputStream(out.toByteArray()));
         loaded.getLocalTranslation().x += loadedPosition;
         loadedPosition += 20;
         rootNode.attachChild(loaded);
         rootNode.updateGeometricState(0, false);
         rootNode.updateRenderState();
      } catch (Exception e) {
         e.printStackTrace();
      }
    }
       
    @Override
    public void simpleUpdate() {
       input.update(tpf);
    }
}



This fix should also be done in jme 1.

related issue http://www.jmonkeyengine.com/jmeforum/index.php?topic=9414.0;topicseen



Maybe simply saving the LightManagement isn't the correct approach.

This needs a bit more investigation and testing :slight_smile:



edit:

just saw that this LightManager stuff shouldn't be needed anymore:

http://www.jmonkeyengine.com/jmeforum/index.php?topic=7799.0

"The light handling was revamped to handle light prioritizing/sorting as part of core (making the old complex LightManagement stuff obsolete) …"

The typo when saving the light in SimpleLightNode is committed.

Could someone apply the fix also to jme1?



The Lightcontroller / Manager stuff needs more testing, it shouldn't be needed anymore in jme2, but in my tests it didn't seem to combine the lights correctly.

But i'm probably doing something wrong :slight_smile:

If its not needed anymore in jME 2 then I say give it the axe! No sense in investing effort to fix something that no longer has a purpose.

Oh… and thanks for fixing that typo bug. Good work.

i should have looked in the tests earlier :slight_smile:

jmetest.util.TestManyLights shows how the sorting is done:


rootNode.sortLights();


Now my test works too :)

So i guess that means the LightManager/LightStateController stuff are not needed anymore in jme 2, should they be marked as @Deprecated with a javadoc hint to Node.sortLights()?

(ok chiming in rather later here)

I would say, yes… anything not needed can be marked deprecated at LEAST.

Deprecated in Java usually means that it still works / makes sense using it.



If it is completely useless (meaning: it doesn't work and it makes no sense using), I vote for getting rid of it.

committed.



There is still the SimpleLightNode typo which should be also fixed in jme 1.