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.