Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

Hi everyone

I have a problem who append not systematicly.

in fact i have multipl object “metro” and i want to switch the camera(cameraNode) form a metro to another via the method.
Its work perfectily like i want but after few switch the app crash… and i have this error message :

déc. 23, 2013 4:21:55 PM com.jme3.app.Application handleError
Grave: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
java.lang.IllegalStateException: Scene graph is not properly updated for rendering.
State was changed after rootNode.updateGeometricState() call.
Make sure you do not modify the scene from another thread!
Problem spatial name: Root Node
at com.jme3.scene.Spatial.checkCulling(Spatial.java:260)
at com.jme3.renderer.RenderManager.renderSubScene(RenderManager.java:647)
at com.jme3.renderer.RenderManager.renderScene(RenderManager.java:640)
at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:974)
at com.jme3.renderer.RenderManager.render(RenderManager.java:1029)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:252)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
at com.jme3.system.lwjgl.LwjglCanvas.runLoop(LwjglCanvas.java:229)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
at java.lang.Thread.run(Thread.java:744)

I read this topic but I do not understand everything.

On this code( afte the init method) you can see the ChangeCamera method.

Thank you for your help.


import com.jme3.animation.LoopMode;
import com.jme3.asset.plugins.FileLocator;
import com.jme3.cinematic.MotionPath;
import com.jme3.cinematic.events.MotionEvent;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.renderer.Camera;
import com.jme3.renderer.Caps;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Quad;
import com.jme3.shadow.BasicShadowRenderer;
import com.jme3.system.JmeCanvasContext;
import com.jme3.ui.Picture;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.control.CameraControl.ControlDirection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SceneofMetro extends SimpleApplication {

private Node pointNode;
private CameraNode camNode;
private MotionPath path;
private Spatial tunnel,point;
private JmeCanvasContext ctx;
private ChaseCamera chaser;
private BasicShadowRenderer bsr;
private Vector3f lightDir = new Vector3f(-1, -1, .5f).normalizeLocal();
private Picture texCockpit;

private Node tunnelNode;

final String assetPath = "D:\\Users\\hmiladi\\Desktop\\PPF12_9_10\\src\\com\\resources\\ANIMATION\\assets";
private  Map<Integer, Metro3D> mapMetro;
private int dernierNumMaterielSuivi;   
 ConcurrentSkipListSet<Integer> maListeCourante = null;


public void simpleInitApp() {

    this.assetManager.registerLocator(assetPath, FileLocator.class);

    mapMetro = new  TreeMap<Integer, Metro3D>();

    point = assetManager.loadModel("Models\\cube\\test4.j3o");
    point.scale(0.001f, 0.001f, 0.001f);
    pointNode = new Node("metronode");
    pointNode.setLocalTranslation(0f, 0f, 0f);

    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
    Geometry ground = new Geometry("ground", new Quad(150, 150));
    ground.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));
    ground.setLocalTranslation(-50, 0, 50);

// Light
    AmbientLight al = new AmbientLight();
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection(new Vector3f(-1f, -1f, -1.0f));

    path = new MotionPath();
    path.addWayPoint(new Vector3f(-50, 0, 0));
    path.addWayPoint(new Vector3f(-35, 0, 0));
    path.addWayPoint(new Vector3f(0, 0, 10));
    path.addWayPoint(new Vector3f(20, 0, -20));
    path.addWayPoint(new Vector3f(30, 0, -35));
    path.addWayPoint(new Vector3f(50, 0, -40));
    path.addWayPoint(new Vector3f(80, 0, -40));
    path.addWayPoint(new Vector3f(20, 0, -80));
    path.enableDebugShape(assetManager, rootNode);

// camera chase
chaser = new ChaseCamera(cam, point);
chaser.setUpVector(new Vector3f(0f, 1f, 0f));


    //creating the camera Node
    camNode = new CameraNode("CamNode",cam);
    camNode.setLocalTranslation(new Vector3f(-10, 3, 0));

    //  Activation des listener clavier


public void animScene(boolean value){
    if (value == true){
        for (Map.Entry<Integer, Metro3D> entry : mapMetro.entrySet()) {
        for (Map.Entry<Integer, Metro3D> entry : mapMetro.entrySet()) {

public void addNumMmaterielToMapMetro(){
    ObservableSession maSessionCourante = Main.listeSessions.get(0);

        //pn attend 30ms  le temps que l'ensemble de la listes soit copier dans mapMetro'
       try {
       } catch (InterruptedException ex) {
           Logger.getLogger(SceneofMetro.class.getName()).log(Level.SEVERE, null, ex);

       maListeCourante =  new ConcurrentSkipListSet<Integer>(maSessionCourante.getMapTrains().keySet());
       Iterator i = maListeCourante.iterator();
           mapMetro.put( (Integer) i.next(), null);

public void addMetro3DToMapMetro(){

    for (Map.Entry<Integer, Metro3D> entry : mapMetro.entrySet()) {
        float position = randRange(0, 100);
        Metro3D monmetro = new Metro3D( entry.getKey(), position, assetManager, rootNode, path);
        mapMetro.put(entry.getKey(), monmetro);

private void detachDernierMetroNode(){
    for (Map.Entry<Integer, Metro3D> entry : mapMetro.entrySet  ()) {
        Iterator i = maListeCourante.iterator();
        if (entry.getKey() == dernierNumMaterielSuivi){          
             // this local varaible  is here in order you that entry.getValue() is a Metro3D
             Metro3D metroTempo =entry.getValue();

public void ChangeCamera(int numMateriel){
boolean camChangee = false;
for (Map.Entry<Integer, Metro3D> entry : mapMetro.entrySet ()) {
// recherche du metro cliqué
Iterator i = maListeCourante.iterator();
if (entry.getKey() == numMateriel){
camChangee = true;
Metro3D metroTempo =entry.getValue();
camNode = new CameraNode(“CamNode”, cam);
camNode.setLocalTranslation(new Vector3f(-10, 2, 0));
camNode.lookAt(metroTempo.getMetroNode().getWorldTranslation(), new Vector3f(0, 1, 0));return;

public float randRange(float min, float max) {
	return min + (float)Math.random() * (max - min);

public void setupBasicShadow(){
if (renderer.getCaps().contains(Caps.GLSL100)){
bsr = new BasicShadowRenderer(assetManager, 1024);

private void setupKeys() {
inputManager.addMapping(“Lefts”, new KeyTrigger(KeyInput.KEY_LEFT));
inputManager.addMapping(“Rights”, new KeyTrigger(KeyInput.KEY_RIGHT));
inputManager.addMapping(“Ups”, new KeyTrigger(KeyInput.KEY_UP));
inputManager.addMapping(“Downs”, new KeyTrigger(KeyInput.KEY_DOWN));
inputManager.addMapping(“Camera”, new KeyTrigger(KeyInput.KEY_C));
inputManager.addMapping(“play_stop”, new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping(“question”, new KeyTrigger(KeyInput.KEY_Q));
inputManager.addMapping(“delete”, new KeyTrigger(KeyInput.KEY_M));
inputManager.addListener(actionListener, new String[]{“play_stop”, “delete”});
inputManager.addListener(analogListener, new String[]{“Ups”,“Rights”,“Lefts”,“Downs”,“question”});
private ActionListener actionListener = new ActionListener() {
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals(“play_stop”) && keyPressed) {
if (name.equals(“delete”) && keyPressed) {
/* getMonliveview().destroy();

private AnalogListener analogListener = new AnalogListener() {
public void onAnalog(String name, float value, float tpf) {
if (name.equals(“Ups”)) {
ChangeCamera( 12130);
if (name.equals(“Downs”)) {
// metroNode.move(0.5f, 0f, 0f);
if (name.equals(“Rights”)) {
// metroNode.move(0f, 0f, -0.05f);
// metroNode.rotate(0, 0.01f, 0);
if (name.equals(“Lefts”)) {
//metroNode.move(0f, 0f, 0.05f);
// metroNode.rotate(0, -0.01f, 0);
if (name.equals(“question”)) {


//////////////// Accesseur mutateur
public AssetManager getAssetManager(){
return this.assetManager;
public JmeCanvasContext getCtx() {
return ctx;

public void setCtx(JmeCanvasContext ctx) {
this.ctx = ctx;

/* public void update(ConcurrentSkipListSet<Integer> maMapObservee) {
////a defiinir

class Metro3D {

private Node metroNode = null;
private Camera camMetro = null;
private Spatial metro = null;
private  int numMateriel;
private float position= 0f;
private MotionEvent motionControl ;
private boolean rameA;
private float vitesse;

public Metro3D(int numMateriel, float position, AssetManager assetManager, Node rootNode, MotionPath path) {

    this.numMateriel =numMateriel;
    this.position = position;
    this.rameA = true;
    this.vitesse =0;

    this.metro = assetManager.loadModel("Models\\cube\\test4.j3o");
    this.metro.scale(1f, 1f, 1f);
    this.metro.rotate(0.0f, 0.0f, 0.0f);
    this.metro.setLocalTranslation(-0f, 0f, 0.0f);
   // metro.setShadowMode(ShadowMode.CastAndReceive);
    this.metroNode = new Node("metronode");
    this.metroNode.setLocalTranslation(-50, 0f, 0f);

    //creation de l'anim  des metro
     motionControl = new MotionEvent(metroNode,path);
     motionControl.setRotation(new Quaternion().fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y));

public void setMetroPosition(float position){

public void  animMetro (boolean value){
    if (value == Boolean.TRUE) {

//////////////// Accesseur mutateur

public float getVitesse(){
return this.vitesse;

public void setVitesse(float nouvelleVitesse) {
this.vitesse = nouvelleVitesse;
public Spatial getMetro() {
return metro;

public void setMetro(Spatial metro) {
this.metro = metro;

public MotionEvent getMotionControl() {
return motionControl;

public void setMotionControl(MotionEvent motionControl) {
this.motionControl = motionControl;

public Node getMetroNode() {
return metroNode;

public void setMetroNode(Node monMetroNode) {
this.metroNode = monMetroNode;

public int getNumMateriel() {
return numMateriel;

public void setNumMateriel(int monNumMateriel) {
this.numMateriel = monNumMateriel;

public Camera getCamMetro(){
return this.camMetro;

public void setCamMetro( Camera maCamera){
this.camMetro = maCamera;



I don’t know how edit my post but i havesend my post quickly,

this is more understandable:

I have a problem that occurs way after some random change camera.

I read this topic(http://hub.jmonkeyengine.org/forum/topic/help-detachchild-spatial-time/) but I do not understand everything.

In fact, I have several objects “metro” and I want to change camera (cameraNode) from a subway to another via the “ChangeCamera” method.
AC works fine, but after some change camera, application crash … and I have this error message: code pasted

sorry to the double post.

Are you changing things from another thread… like the swing thread or something?

thanks for your answer ! :slight_smile:

In fact I do not master threads but I detach the camNode of a Metro3D like “oldMetro3D.getMetroNode().detachChild (camNode).” Then I’m tying the camNode has another Metro3D like “newMetro3D.getMetroNode().attachChild(camNode);”

@hedi said: thanks for your answer ! :)

In fact I do not master threads but I detach the camNode of a Metro3D like “oldMetro3D.getMetroNode().detachChild (camNode).” Then I’m tying the camNode has another Metro3D like “newMetro3D.getMetroNode().attachChild(camNode);”

I have no idea what this means… or if you are acknowledging what @pspeed said, but he is correct.

You need to enqueue changes to the scene graph through a Callable or Future if they are being fired off from another thread.

The reason the crash is intermittent is only luck… sometimes the change happens when the scene is unlocked, other times… you’re not so lucky.

thank you pspeed and t0neg0d

I enqueue my method and it work fine !