Can't add more geometries after first update?

I am getting this exception:



SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,6,main]

java.lang.IllegalStateException: Scene graph is not properly updated for rendering.

Make sure scene graph state was not changed after

rootNode.updateGeometricState() call.



Which kinda sucks since that method is called every time the game updates, how can I add more geometries without having to worry about this?

Did you create your own rootNode or viewport or something?



By default, you can modify the scene graph all you want in update(). What that error is telling you is that you’ve somehow modified it after updateGeometricState() was called but before the start of the next update() pass. Or that somehow the rootNode that you are changing is not the one automatically having that called.

Had the same problem. http://hub.jmonkeyengine.org/groups/general-2/forum/topic/how-to-do-asynchronous-scenegraph-updates-in-jme3/ Solved it by adding a sceneupdater which gets called instead of attachChild. Updater queues the requests to be attached and does it so in the update loop of the application (simpleUpdate)

1 Like

In fact if this is a threading issue then there is already a queue built into JME exactly for that purpose.

http://hub.jmonkeyengine.org/javadoc/com/jme3/app/Application.html#enqueue(java.util.concurrent.Callable)



I may have assumed incorrectly since threading was never mentioned and there are other reasons that exception can be thrown. That would be useful information to know. :slight_smile:

1 Like
pspeed said:
In fact if this is a threading issue then there is already a queue built into JME exactly for that purpose.
http://hub.jmonkeyengine.org/javadoc/com/jme3/app/Application.html#enqueue%28java.util.concurrent.Callable%29

I may have assumed incorrectly since threading was never mentioned and there are other reasons that exception can be thrown. That would be useful information to know. :)

ghoust said:
Had the same problem. http://hub.jmonkeyengine.org/groups/general-2/forum/topic/how-to-do-asynchronous-scenegraph-updates-in-jme3/ Solved it by adding a sceneupdater which gets called instead of attachChild. Updater queues the requests to be attached and does it so in the update loop of the application (simpleUpdate)



Thanks, Ill look into these.

The exception occurs when I run a script that creates new geometries. So not in the update(), I guess that's the problem and a queue should solve it :)

I just tried the enqueue method but it didn’t solve my problem, I guess I somehow managed to duplicate my rootNode, I guess I’ll go check every class. just to be sure, this is how enqueue is meant to be used right?

[java] public Part(){ // constructor

super();

final Part thisPart = this;

MainClass.mainClass.enqueue(new Callable(){

@Override

public Object call() throws Exception {

thisPart.setName(“Part”);

thisPart.geometry = new Geometry(thisPart.getName(),new Box(thisPart.Position,thisPart.Size.x,thisPart.Size.y,thisPart.Size.z)); // geometry is a field in the superclass

thisPart.setMaterial(m.getMaterial()); // just a wrapper for the geometry

thisPart.m_metatable = MetaTables.Part(); // metatable of this Lua object

thisPart.attachChild(thisPart.geometry);

MainClass.mainClass.getRootNode().attachChild(thisPart); //mainClass is a static instance of MainClass that extends simpleApplication

return null;

}

});

}[/java]

If you can somehow reduce to a smaller reproduceable test case that can be posted then we might be able to help further. If you don’t have separate threads then you shouldn’t need the enqueue stuff.

Writing a test case would be rather difficult, but in short.



I have a base class that is the superclass of every other class. I’m not using the normal classes directly because my classes need to be Lua compatible. This base class has static fields for the rootNode and assetManager that have been set from the simpleInitApp() method.



When I run a script directly from simpleInitApp() there is no problem and everything works fine, but when I run the script while the game is running I am getting this error.

If you can’t reproduce it in a test case it seems this is growing way over your head ^^

raven5887 said:
When I run a script directly from simpleInitApp() there is no problem and everything works fine, but when I run the script while the game is running I am getting this error.


How are you "running the script while the game is running"? You were very explicit about the simpleInitApp(). Where are you running the other script?
pspeed said:
How are you "running the script while the game is running"? You were very explicit about the simpleInitApp(). Where are you running the other script?


I made a build-in editor so I could add things to the game from within the game itself. Therefor it seemed likely that the exception is caused by asynchrony but running it in enqueue didn't solve it so I'm a bit stuck now

Well I have been experimenting with the enqueue method but it simply doesn’t seem to be working, while a simple static ArrayLists in the main class that checks every update if the list is empty and if its not adds all geometries in the list to the rootNode works perfectly fine.

I’m not entirely sure how you set things up but I imagine there’s still an update() method somewhere. If that’s the case then make yourself a class that serves as a queue. At each frame check if there’s work to be done and if so, add it before the super.update();



That -should- work.

he said he is running a script = he is running code in another thread. Which is why it doesn’t work.

So? That second thread can modify the queue class. It has to be synchronized but it’s doable. I use something similar except it doesn’t deal with geometries.

raven5887 said:
Well I have been experimenting with the enqueue method but it simply doesn't seem to be working, while a simple static ArrayLists in the main class that checks every update if the list is empty and if its not adds all geometries in the list to the rootNode works perfectly fine.


You will have to show us code. Enqueing a callable on the Application's update queue definitely works... it's about as simple as it gets code-wise so I don't think there is much that can go wrong on the JME side.

The first thing the update loop does is pull the Callables from that queue and run them. So if the code that modifies the scene graph is in that callable's run method then it will work.

That's a lot like what your array list is doing except the threading issues have already been worked out for. For example, an array list without synchronization will cause issues... and with sync it could be a performance bottleneck.

[java]static protected ArrayList<Geometry> GeometryQueue = new ArrayList<Geometry>();



@Override

public void simpleUpdate(float tpf) {

if(!GeometryQueue.isEmpty()){

queue.ready = false;

Iterator<Geometry> it = GeometryQueue.iterator();

while(it.hasNext()){

rootNode.attachChild(it.next());

}

GeometryQueue.clear();

queue.ready = true;



if(!queue.waitingQueue.isEmpty()){

Iterator<Geometry> it2 = queue.waitingQueue.iterator();

while(it2.hasNext()){

GeometryQueue.add(it2.next());

}

}

}

}



public static class queue{

protected static boolean ready = true;

protected static ArrayList<Geometry> waitingQueue = new ArrayList<Geometry>();



public static void addGeometry(Geometry g){

if(ready){

GeometryQueue.add(g);

}else{

waitingQueue.add(g);

}

}

}[/java]



Then mainClass.queue.addGeometry(geometry); to add something to the queue. It can probably be improved but for now this works good enough :stuck_out_tongue:

Your geometry queue is not thread safe. If you are really adding things from another thread then you will potentially get random corruption. If you want to go that route then why not just use a single java.util.concurrent.ConcurrentLinkedQueue?



Anyway, I meant show the enqueue code that wasn’t working.



To work, it should have looked something like:

[java]

Application.enqueue( new MyGeometryAdder( rootNode, child );



class MyGeometryAdder {

private Node parent;

private Spatial child;

public MyGeometryAdder( Node parent, Spatial child ) {

this.parent = parent;

this.child = child;

}

public Object call() {

parent.attachChild(child);

return null;

}

}

[/java]



Though if you frequently add things in bulk you could adjust that to hold a list or whatever. I tried to make it general by also supplying the parent instead of relying on being able to get at the rootNode directly.

Hmm, I used this code:

[java]MainClass.mainClass.enqueue(new Callable(){

@Override

public Object call() throws Exception {

thisPart.setName(“Part”);

thisPart.geometry = new Geometry(thisPart.getName(),new Box(thisPart.Position,thisPart.Size.x,thisPart.Size.y,thisPart.Size.z)); // geometry is a field in the superclass

thisPart.setMaterial(m.getMaterial()); // just a wrapper for the geometry

thisPart.m_metatable = MetaTables.Part(); // metatable of this Lua object

thisPart.attachChild(thisPart.geometry);

MainClass.mainClass.getRootNode().attachChild(thisPart); //mainClass is a static instance of MainClass that extends simpleApplication

return null;

}

});[/java]

so that might have been the problem, and since I understand the way I do things now isn’t very safe I’ll try it out your way

Note: when I put Application.enqueue… it was sort of pseudo code. In your version this would be MainClass.mainClass.enqueue.



Edit: and if it doesn’t work then we can help you get it to work if we know how it doesn’t work.