Illegal State Exception but only one thread

I have this error

Uncaught exception thrown in Thread[LWJGL.Renderer Thread, 5,main]
IllegalStateException: Scene graph is not properly updated for rendering. State was changed after rootNode.updateGometricState() call.
Make sure you do not modify the scene from another thread!
Problem spatial name: OffScreen

But the fact is that I only have one thread running… Perhaps is the order of call of one method…

Here the code :

package render_3d.offScreen.tests;

import java.nio.FloatBuffer;
import java.util.LinkedList;

import render_3d.MyChaseCamera;
import render_3d.Render3d;
import render_3d.offScreen.OffScreen;
import render_3d.offScreen.UIComponent;
import render_3d.offScreen.UserInterface;
import render_3d.offScreen.Window;
import render_3d.screen.Screen;
import render_3d.screen.ScreenGroup;
import render_3d.systemstate.Side;
import render_3d.systemstate.SystemState;
import render_3d.systemstate.SystemStateLoader;
import shapes.polygonShape.PolygonShape;
import shapes.shape.Group;
import shapes.shape.MyPoint;

import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.debug.Arrow;
import com.jme3.scene.shape.Box;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;
import com.jme3.util.BufferUtils;

/**

  • This test renders a scene to a texture, then displays the texture on a cube.
    */
    public class TestOffScreen extends Render3d {

    LinkedList<Screen> screenList;
    LinkedList<SystemState> systemStates;
    LinkedList<UserInterface> UIs;
    PolygonShape ps1;
    PolygonShape ps2;

    SystemState ss1;
    private MyChaseCamera chaseCam;

    public TestOffScreen(LinkedList<Group> entityList) {
    super(entityList);
    // TODO Auto-generated constructor stub
    }
    public TestOffScreen(){
    super(null);
    systemStates = new LinkedList<SystemState>();
    UIs = new LinkedList<UserInterface>();
    screenList = new LinkedList<Screen>();
    }

    public static void main(String[] args){
    TestOffScreen app = new TestOffScreen();
    app.setShowSettings(false);
    app.start();
    }

    public void initChaseCamera(){
    // Disable the default first-person cam
    flyCam.setEnabled(false);
    // Enable a chase cam
    chaseCam = new MyChaseCamera(cam, inputManager);
    chaseCam.setMinVerticalRotation(-FastMath.PI/2);
    chaseCam.setMaxVerticalRotation(FastMath.PI/2);
    chaseCam.setInvertVerticalAxis(true);
    chaseCam.setRotationSpeed(4f);
    chaseCam.setMinDistance(2f);
    chaseCam.setMaxDistance(30f);
    chaseCam.setDefaultHorizontalRotation(FastMath.PI/2);
    chaseCam.setDefaultVerticalRotation(0f);
    chaseCam.setDefaultDistance(9f);
    chaseCam.setDragToRotate(true);
    }

    public void initPolygonShapes(){
    MyPoint p1 = new MyPoint(0,0);
    MyPoint p2 = new MyPoint(0,150);
    MyPoint p3 = new MyPoint(100,150);
    MyPoint p4 = new MyPoint(100,0);
    LinkedList<MyPoint> points = new LinkedList();
    points.add(p1);
    points.add(p2);
    points.add(p3);
    points.add(p4);

     MyPoint p12 = new MyPoint(0,0);
     MyPoint p22 = new MyPoint(0,150);
     MyPoint p32 = new MyPoint(100,150);
     MyPoint p42 = new MyPoint(100,0);
     LinkedList&lt;MyPoint&gt; points2 = new LinkedList();
     points2.add(p12);
     points2.add(p22);
     points2.add(p32);
     points2.add(p42);
    
    
     ps1 = new PolygonShape(points);
     ps2 = new PolygonShape(points2);
     ps2.move(0, 150.0);
     Group g1 = new Group(ps1);
     System.out.println("group bounds: "+g1.getBounds2D()[0]+" "+
     		g1.getBounds2D()[1]+" "+
     		g1.getBounds2D()[2]+" "+
     		g1.getBounds2D()[3]+" ");
     g1.addShape(ps2);
     System.out.println("group bounds after: "+g1.getBounds2D()[0]+" "+
     		g1.getBounds2D()[1]+" "+
     		g1.getBounds2D()[2]+" "+
     		g1.getBounds2D()[3]+" ");
    

    }

    @Override
    public void simpleInitApp() {
    initChaseCamera();
    attachCoordinateAxes(new Vector3f(-3f,0f,0f));
    initUI();
    initPolygonShapes();

     Screen s1 = new Screen(ps1,this);
     screenList.add(s1);
     
     Screen s2 = new Screen(ps2,this);
     screenList.add(s2);
     
     initSystemState();
     loadSystemState();
     
     s1.makeScreen();
     rootNode.attachChild(s1);
     s2.makeScreen();
     rootNode.attachChild(s2);
    

    }

    @Override
    public void simpleUpdate(float tpf){

    }

    public void initSystemState(){
    ss1 = new SystemState(“SS1”);
    ss1.addMapping(0, screenList.get(0).getId(), Side.FRONT);
    ss1.addMapping(0, screenList.get(1).getId(), Side.FRONT);
    }

    public void loadSystemState(){
    SystemStateLoader ssl = new SystemStateLoader(this, UIs, screenList);
    ssl.load(ss1);
    }

    public void initUI(){
    ui = new UserInterface();
    ui.setWidth(2f);
    ui.setHeight(6f);
    Window w = new Window(this.assetManager, 1f,0.6f,1);
    ui.addComponent(w);
    this.UIs.add(ui);
    }

    public void initOffScreen(){
    offScreen = new OffScreen(this, ui.getWidth(), ui.getHeight(), ui);
    rootNode.attachChild(offScreen);
    offScreen.setLocalTranslation(6f,0f, 0f);
    }

    private Geometry putShape(Mesh shape, ColorRGBA color){
    Geometry g = new Geometry(“coordinate axis”, shape);
    Material mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
    mat.getAdditionalRenderState().setWireframe(true);
    mat.setColor(“Color”, color);
    g.setMaterial(mat);
    rootNode.attachChild(g);
    return g;
    }

    private void attachCoordinateAxes(Vector3f pos){
    Arrow arrow = new Arrow(Vector3f.UNIT_X);
    arrow.setLineWidth(4); // make arrow thicker
    putShape(arrow, ColorRGBA.Red).setLocalTranslation(pos);

     arrow = new Arrow(Vector3f.UNIT_Y);
     arrow.setLineWidth(4); // make arrow thicker
     putShape(arrow, ColorRGBA.Green).setLocalTranslation(pos);
    
     arrow = new Arrow(Vector3f.UNIT_Z);
     arrow.setLineWidth(4); // make arrow thicker
     putShape(arrow, ColorRGBA.Blue).setLocalTranslation(pos);
    

    }

}

And this other class called in loadSystemState() mehtod:

package render_3d.systemstate;

import java.util.LinkedList;

import render_3d.Render3d;
import render_3d.offScreen.OffScreen;
import render_3d.offScreen.UserInterface;
import render_3d.screen.Screen;
import render_3d.screen.ScreenGroup;

/**

  • This class is a tool to load system states

  • it Creates the ScreenGroups (with correct textures) and adds corresponding screen in it.

  • A screenGroup can only contain 1 user interface ( a screen group represents only one “face” of a screen)

  • */
    public class SystemStateLoader {
    private Render3d app;
    private LinkedList<UserInterface> UIs;
    private LinkedList<Screen> screens;

    public SystemStateLoader(Render3d app){
    this.app = app;
    }

    public SystemStateLoader(Render3d app, LinkedList<UserInterface> UIs, LinkedList<Screen> screens){
    this.app = app;
    this.UIs = UIs;
    this.screens = screens;
    }

    public void load(SystemState ss){
    //for all user interfaces. nb: one UI correspond to one screenGroup
    for(Integer i: ss.getUIs()){
    UserInterface ui = getUIById(i);
    //create the corresponding OffScreen
    OffScreen offScreen = new OffScreen(app, ui.getWidth(), ui.getHeight(), ui);

     	//create the corresponding ScreenGroup
     	ScreenGroup sg = new ScreenGroup();
     	sg.setOffScreen(offScreen);
     	//add all the corresponding screens in this screengroup
     	for (UIToScreenMapEntry entry: ss.getMap()){
     		if (entry.getUiId() == i){
     			Screen s = getScreenById(entry.getScreenId());
     			if (entry.getSide() == Side.FRONT){
     				s.setFrontScreenGroup(sg);
     				sg.addChild(s,Side.FRONT);
     			}
     			else {
     				s.setBackScreenGroup(sg);
     				sg.addChild(s,Side.BACK);
     			}
     		}
     	}
     	
     }
    

    }

    /**

    • returns UserInterface object knowing its ID
    • */
      public UserInterface getUIById(int uiId){
      for (UserInterface ui : UIs){
      if (ui.getId() == uiId) return ui;
      }
      return null;
      }

    /**

    • returns a screen object knowing its ID
      */
      public Screen getScreenById(int screenId){
      for (Screen s: screens){
      if (s.getId() == screenId) return s;
      }
      return null;
      }

    public LinkedList<UserInterface> getUIs() {
    return UIs;
    }

    public void setUIs(LinkedList<UserInterface> uIs) {
    UIs = uIs;
    }

    public LinkedList<Screen> getScreens() {
    return screens;
    }

    public void setScreens(LinkedList<Screen> screens) {
    this.screens = screens;
    }

}

Do you see something I did wrong in the order of call or something…?

Thanks

There is nothing obvious that jumps out from the code submitted. Of course, we don’t actually see where the “Offscreen” node is created.

There must be another thread somewhere. Do you run this as part of a swing application by any chance? What is Render3d?

Initialization order doesn’t matter, really. The way this error is triggered can only be caused by three things:

  1. you’ve somehow modified a spatial/node outside the normal update() loop.
  2. you’ve somehow modified a spatial/node from another thread (same as 1 really except more random)
  3. you are managing your own viewports but aren’t managing them properly by calling the appropriate Spatial.updateLogicalState() and updateGeometricState() on the Viewport’s root.

In fact In the class SystemStateLoader I create an offscreen and I forgot to add it to my scene (because I want it to be displayed on the scene).

I don’t know exactly why but adding offScreen to the scene (rootNode.attachChild(offScreen) ) solved the problem.