Make sure you do not modify the scene from another thread!

Got this error:

GRAVE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.lang.UnsupportedOperationException: Compare function result changed! Make sure you do not modify the scene from another thread!
    at com.jme3.util.ListSort.mergeHigh(
    at com.jme3.util.ListSort.mergeRuns(
    at com.jme3.util.ListSort.mergeForceCollapse(
    at com.jme3.util.ListSort.sort(
    at com.jme3.renderer.queue.GeometryList.sort(
    at com.jme3.renderer.queue.RenderQueue.renderGeometryList(
    at com.jme3.renderer.queue.RenderQueue.renderQueue(
    at com.jme3.renderer.RenderManager.renderViewPortQueues(
    at com.jme3.renderer.RenderManager.flushQueue(
    at com.jme3.renderer.RenderManager.renderViewPort(
    at com.jme3.renderer.RenderManager.render(
    at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(
    at com.jme3.system.lwjgl.LwjglDisplay.runLoop(

Because of this:

target.getLocalTranslation().clone().subtract(spatial.getLocalTranslation(), delta);

Why is that?

  1. the target vector is cloned
  2. the spatial vector is untouched
  3. the delta vector is not used for storing spatial positions…

You use getLocalTranslation…

First, how do you know that’s the line at issue?

Second, you know that even accessing unthreadsafe fields from another thread is bad news, right? I mean, it’s not the cause of this issue but every one of those gets and the clone may return really strange results depending on the state of the local thread memory and so on.

If a spatial is attached to the scene, do not access it at all from another thread. Not even a little. Down that path is peril.

1 Like

I also think we’d have to know where delta came from and what you are doing with it.

Indeed. Why is it bad?

Because when commented the error go away

It is done within the controlUpdate() of a Control… if I remember correctly it should be ok.

Here you go:

public abstract class AbstractMover extends AbstractControl{
    int speed_unit=SPRITE_SIZE;
    int speed_unit_bg=(int)(SPRITE_SIZE*.75f);
    Vector3f delta=new Vector3f();
    abstract public Vector3f getDelta(float tpf);
    protected void controlUpdate(float tpf) {
        if (spatial.getLocalTranslation().z == -1) {
            spatial.move(delta.x * speed_unit_bg, delta.y * speed_unit_bg, 0);
        } else {
            spatial.move(delta.x * speed_unit, delta.y * speed_unit, 0);
    protected void controlRender(RenderManager rm, ViewPort vp) {


public class FollowSimonettaMover extends AbstractMover{

    float startPos=0;
    boolean firstMove=false;
    Spatial target;
    public FollowSimonettaMover(String hPos){
    public Vector3f getDelta(float tpf) {
        if (!firstMove){
            spatial.move(0, MS_HEIGHT*startPos, 0);
        target.getLocalTranslation().clone().subtract(spatial.getLocalTranslation(), delta);
        return delta;

Because you’re using multiple threads.

Probably I’m missing something obvious, but…

  1. as I said above, I think I’m not accessing spatials from outside the update loop; what makes you think otherwise?
  2. even if I were, what makes “local” worse than “global”?

Yes, but when you comment it out then delta is also 0. If you want to see if that line is really the error then comment it out but set delta to something.

I actually don’t know why you are getting the error. Do you have other threads that are accessing the scene graph? (ie: something other than this code which clearly seems to be fine?)

The error indicates that something about the geometry changed while the sort was happening. So far, every time this error has shown up, eventually that turns out to be the case with enough investigation.

This solved the issue:

        target.getLocalTranslation().clone().subtract(spatial.getLocalTranslation(), delta);
        return delta;


Maybe you meant normalizeLocal().

…either way, it shouldn’t have caused the error you saw.

Random notes on the rest of the code now that I’m not looking directly at the problem anymore:

  1. Just kind of a sylistic/idiomatic issue… you might consider avoiding a ‘get’ method with side-effects. In this case, you end up resetting the reference to itself unnecessarily. getDelta() has already reset the value of delta. Maybe pass delta to the method or rename it to recalculateDelta() and not have it return anything.

  2. you never seem to use tpf anywhere, thus your movement speed is frame rate dependent. Maybe you just haven’t gotten to it yet since you do pass it around…but then don’t use it yet.

Edit: I wonder what delta’s value actually was in the case where you got the ‘error that should be unrelated’.

This made my code way cleaner… really didn’t realize something that obvious :slight_smile:

        System.out.println("1 "+delta);
        target.getLocalTranslation().clone().subtract(spatial.getLocalTranslation(), delta);
        System.out.println("2 "+delta);

Gives this output:

1 (0.0, 0.0, 0.0)
2 (-1919.7385, -28.0, 0.0)
1 (-1919.7385, -28.0, 0.0)
2 (489534.75, 7140.0, 0.0)
1 (489534.75, 7140.0, 0.0)
2 (-1.24831352E8, -1820700.0, 0.0)
1 (-1.24831352E8, -1820700.0, 0.0)
2 (3.18319944E10, 4.64278496E8, 0.0)
1 (3.18319944E10, 4.64278496E8, 0.0)
2 (-8.1171584E12, -1.18391013E11, 0.0)
1 (-8.1171584E12, -1.18391013E11, 0.0)
2 (2.06987533E15, 3.01897089E13, 0.0)
1 (2.06987533E15, 3.01897089E13, 0.0)
2 (-5.2781822E17, -7.6983756E15, 0.0)
1 (-5.2781822E17, -7.6983756E15, 0.0)
2 (1.3459365E20, 1.96308579E18, 0.0)
1 (1.3459365E20, 1.96308579E18, 0.0)
2 (-3.4321379E22, -5.005869E20, 0.0)
1 (-3.4321379E22, -5.005869E20, 0.0)
2 (8.7519517E24, 1.2764966E23, 0.0)
1 (8.7519517E24, 1.2764966E23, 0.0)
2 (-2.2317476E27, -3.2550663E25, 0.0)
1 (-2.2317476E27, -3.2550663E25, 0.0)
2 (5.6909563E29, 8.300419E27, 0.0)
1 (5.6909563E29, 8.300419E27, 0.0)
2 (-1.4511939E32, -2.1166068E30, 0.0)
1 (-1.4511939E32, -2.1166068E30, 0.0)
2 (3.7005444E34, 5.3973474E32, 0.0)
1 (3.7005444E34, 5.3973474E32, 0.0)
2 (-9.4363885E36, -1.3763236E35, 0.0)
1 (-9.4363885E36, -1.3763236E35, 0.0)
2 (Infinity, 3.5096253E37, 0.0)

Then the error…

Infinity is not supported in this case.

Yes, but now we know why that strange error kicks out of sorting and we could potentially do better.

Thanks @Pesegato, that exception has bugged me ever since we converted to different sorting and it’s always worried me that there was a non-multithreading cause that we just hadn’t found yet. Now we know that there is… and I can even explain why. That’s great.