Control/AppState Update BUG

Hello devs!

I found a bug with Update of Control and AppState.

For Control:
If I attach a Control class to RootNode i got incorrect update of a control.

My code:
[java]
// WORKS OK!
Node tempNode = new Node();
rootNode.attachChild(tempNode);
transformManager = new EditorTransformManager(this.app, this);
tempNode.addControl(transformManager);

    // UPDATE BUG IF I DO LIKE THIS. OR IF I CONVERT THE CONTROL TO APPSTATE.
    transformManager = new EditorTransformManager(this.app, this);
    rootNode.addControl(transformManager);

[/java]

Here is my video demonstration with a custom node(0:00 time) and with rootNode(1:10 time):
__
[video]http://www.youtube.com/watch?v=JBM-jLIK6Dk&feature=youtu.be[/video]

__
My manipulator flies like crazy!

Code of controlUpdate() method:
[java]

protected void updateTransformWidget(Transform center) {
    if (center != null) {
        Vector3f vec = center.getTranslation().subtract(app.getCamera().getLocation()).normalize().multLocal(app.getCamera().getFrustumNear() + 0.1f);
        transformTool.setLocalTranslation(app.getCamera().getLocation().add(vec));
        transformTool.setLocalRotation(center.getRotation());
    }
}


@Override
protected void controlUpdate(float tpf) {

        // Update transform tool
        transformTool.detachAllChildren();
        if (transformType == transformType.MoveTool) {
            transformTool.attachChild(moveTool);
        } else if (transformType == transformType.RotateTool) {
            transformTool.attachChild(rotateTool);
        } else if (transformType == transformType.ScaleTool) {
            transformTool.attachChild(scaleTool);
        }
        updateTransformWidget(selectionTransformCenter);  // the main magic

    } else if (base.getSelectionManager().getSelectionList().size() == 0) {
        transformTool.detachAllChildren();
    }

}
[/java]

Here is the classes I tested:
https://code.google.com/p/simple-world-editor/source/browse/trunk/SimpleWorldEditor/src/com/swe/EditorBaseManager.java
https://code.google.com/p/simple-world-editor/source/browse/trunk/SimpleWorldEditor/src/com/swe/transform/EditorTransformManager.java
Entire code - https://code.google.com/p/simple-world-editor/


For AppStates:
I converted EditorTransformManager Control into AppState and i got the same bug. Appstate’s update works with the same issue if i attach a Control class to the rootNode. :frowning:
I tested the Appstate like this:

[java]
// UPDATE BUG IF I DO LIKE THIS. OR IF I CONVERT THE CONTROL TO APPSTATE.
transformManager = new EditorTransformManager(this.app, this);
app.getAppstateManager().attach(transformManager);
[/java]

Thanks.

Can you describe the problem? I can’t watch a video right now.

I guarantee that your control and app state updates are called correctly so your problem is elsewhere.

@pspeed said: Can you describe the problem? I can't watch a video right now.

I guarantee that your control and app state updates are called correctly so your problem is elsewhere.

i’ll try to make a simple testCase to check if this is my issue of this is the bug then. I’ll try to make it tomorrow.

Step 1: put System.out.println() calls at the top of your update methods.

Step 2: figure out what was wrong in your code after you see a ton of printlns in your console.

Remove all faulty assumptions and then the answer will be clear.

1 Like
@pspeed said: Step 1: put System.out.println() calls at the top of your update methods.

Step 2: figure out what was wrong in your code after you see a ton of printlns in your console.

Remove all faulty assumptions and then the answer will be clear.

Ok, i did the testCase and found the issue. The bug happens with ChaseCamera.
If I use ChaseCamera and attach a Control to rootNode then i get the bug with update.

And also iget the same bug if i convert the Control to AppState.

Here is my another video demonsteration:
__

[video]http://www.youtube.com/watch?v=x87eUW5LRvw&feature=youtu.be[/video]
__
_

Here is my code:
There are 2 classes

AATest.java:
[java]
package com.swe;

import com.jme3.app.SimpleApplication;
import com.jme3.font.BitmapText;
import com.jme3.input.ChaseCamera;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;

public class AATest extends SimpleApplication implements AnalogListener{

public static void main(String[] args) {
    AATest app = new AATest();
    AppSettings aps = new AppSettings(true);
    aps.setVSync(true);
    app.setSettings(aps);
    app.setShowSettings(false);
    app.start();
}

private Node cameraNode;

@Override
public void simpleInitApp() {
    flyCam.setMoveSpeed(50f);
    flyCam.setEnabled(false);
    
    cameraNode = new Node();
    rootNode.attachChild(cameraNode);
    
    ChaseCamera cama = new ChaseCamera(cam, cameraNode, inputManager);
    cama.setToggleRotationTrigger(new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE));
    cama.setEnabled(true);
    
    AAControl aps = new AAControl(this);
    
    Node tempNode = new Node();
    rootNode.attachChild(tempNode);
    
    // here is a BUG if I attach the Control to RootNode
    // but with tempNode the Control work OK!
    rootNode.addControl(aps); 
    
    
    setupKeys();
    generateBoxes();
    
    // gui
     guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
    BitmapText ch = new BitmapText(guiFont, false);
    ch.setSize(guiFont.getCharSet().getRenderedSize());
    ch.setText("Press RightClick"); // crosshairs
    ch.setColor(new ColorRGBA(1f,0.8f,0.1f,1f));
    ch.setLocalTranslation(settings.getWidth()*0.3f,settings.getHeight()*0.1f,0);
    guiNode.attachChild(ch);
}


private void setupKeys() {
    //Set up keys and listener to read it

   String[] mappings = new String[]{
        "MoveCameraHelper",
    };

    inputManager.addMapping("MoveCameraHelper", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
    inputManager.addListener(this, mappings);
}


private void generateBoxes() {
    Box b = new Box(Vector3f.ZERO, 0.3f, 0.3f, 0.3f);
    Geometry geom = new Geometry("Box", b);
    geom.updateModelBound();

    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
    mat.setColor("Color", ColorRGBA.Red);
    geom.setMaterial(mat);
    geom.setLocalTranslation(0,2,1);
    
    int numClones = 1000;
    Node instNodes = new Node();
    
    for (int i=0; i<numClones; i++) {
        Geometry gm = geom.clone(false);
        gm.setName("instance"+i);
        gm.setLocalTranslation((float) Math.random() * 200.0f - 50f,(float) Math.random() * 100.0f,(float)Math.random() * 200.0f - 50f);
        gm.rotate(0, (float) Math.random() * (float)Math.PI, 0);
        instNodes.attachChild(gm);
    }
    
    rootNode.attachChild(instNodes);
}

@Override
public void simpleUpdate(float tpf){
}

@Override
public void onAnalog(String name, float value, float tpf) {
    if (name.equals("MoveCameraHelper")) {
        cameraNode.move(cam.getLeft().normalize().mult(2f));
    }
}

}

[/java]

And here is the AAControl.java:
[java]
/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.
    */
    package com.swe;

import com.jme3.app.Application;
import com.jme3.app.FlyCamAppState;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.shape.Sphere;

/**
*

  • @author mifth
    */
    public class AAControl extends AbstractControl {

    private Node root;
    private Geometry geom;
    private Application app;

    public AAControl(Application app) {

     //TODO: initialize your AppState, e.g. attach spatials to rootNode
     //this is called on the OpenGL thread after the AppState has been attached
     this.app = app;
     root = (Node) app.getViewPort().getScenes().get(0);
    
    
     Sphere b = new Sphere(50, 50, 1);
     geom = new Geometry("Sphere", b);
    
    
     Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
     geom.setMaterial(mat);
     geom.setLocalTranslation(0, 2, 1);
     root.attachChild(geom);
    
     DirectionalLight dl = new DirectionalLight();
     dl.setDirection(new Vector3f(-0.8f, -0.6f, -0.08f).normalizeLocal());
     dl.setColor(new ColorRGBA(1, 1, 1, 1));
     root.addLight(dl);
    
     app.getStateManager().getState(FlyCamAppState.class).getCamera().setMoveSpeed(30);
     app.getViewPort().setBackgroundColor(ColorRGBA.Gray);
    

    }

    @Override
    protected void controlUpdate(float tpf) {
    Camera cam = app.getCamera();
    Vector3f vec = cam.getLocation().add(cam.getDirection().normalize().mult(8f));
    geom.setLocalTranslation((vec));
    }

    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {
    // throw new UnsupportedOperationException(“Not supported yet.”); //To change body of generated methods, choose Tools | Templates.
    }
    }

[/java]

Just run the test and you will see the bug with the update of a Control(attached to rootNode) or AppState.
Just try to change like this:

[java]
rootNode.addControl(aps); // BUG
// OR
tempNode.addControl(aps); // OK
[/java]

So I guess this is the issue either with engineUpdates of Controls/AppStates or the issue with ChaseCamera.

@nehon i think it will be interesting for you too.

Thanks.

1 Like

You still haven’t described what the bug is.

I can guarantee you that update is always called, though.

Edit: because what do you expect to happen when the camera and the root node are locked together?!?

@pspeed said: You still haven't described what the bug is.

I can guarantee you that update is always called, though.

Edit: because what do you expect to happen when the camera and the root node are locked together?!?

Can you simply run my testCase above there are only 2 classes? Code is pretty simple. I place a sphere model before the camera every update. And the sphere is placed not correctly if i attach the Control to rootNode.

Just take my testCase and switch this:
[java]
rootNode.addControl(aps); // BUG
// OR
tempNode.addControl(aps); // OK
[/java]

You will see no synchronizetion of a sphere and camera if the Control will be attached to rootNode.

@pspeed said:

Edit: because what do you expect to happen when the camera and the root node are locked together?!?

This bug happens wit AppState too if I convert Control to the AppState. This is a real bug i think, which should be investigated. If you want I can make the same testCase with an AppState. And there will be the same issue. :frowning:

Maybe someday someone else will run your test case. I’m not paid enough to do it and don’t have the time if you can’t even properly describe the issue. Though at least your last post FINALLY describes some of it. It’s probably enough to go on at least.

I can state with 100000% accuracy that update is being called, though. So there is some misconception on your part with the order events are happening. You are relying on a particular order for things to happen and it’s not happening in that order. But the update methods are absolutely being called and they are being called in a really consistent order… it’s just not the order you expect.

ie: bug is in your code. If you’d debugged these updates to see if they were being called you would see that they are not being called in an order that will work for you.

Updates are called like this:
-app states are all updated
-simpleUpdate()
-root node controls are updated, then children controls are updated, etc. Depth first, preorder traversal.

So, when you attach a chase camera to a child of the root node, it doesn’t get updated until AFTER the root node. Any controls on the root node would not see the change until the next update. App states would not see the change until the next update.

Issue reporting advice:
The absolute minimum for reporting a problem is to describe the problem. You must have this. Don’t make us watch a video unless it clarifies something. Describe the issue in text and so many times it saves us time because we can spot the problem right away. In your case, you’ve run into this exact same issue at least once before and if you’d described in your first post what you waited four or five posts for, I could have told you exactly what the problem was in 20 seconds.

Test cases and videos are awesome but you at least have to tell us what the problem is or you will get far less people interested in looking into it. Between the description and your test case code, I was able to spot the problem right away without running it. I can tell you that I’ve only actually run one test case in almost three years of solving problems here. Usually the description and the code are enough.

@pspeed said: ... .

Sorry for my poor description. That’s because of my english is not so good and i thought i already described the update issue. Yes, updateOrder changes for AAContorl.java if it’s attached to rootNode or created as AppState.

You described the Updates order - it makes many sense.
There is a question for possible fix of my testCase: Is it possible to update manuully single Control/AppState after the rootNode update? Or change a separete Control/AppState updateOrder?

<cite>@mifth said:</cite> Sorry for my poor description. That's because of my english is not so good and i thought i already described the update issue. Yes, updateOrder changes for AAContorl.java if it's attached to rootNode or created as AppState.

You described the Updates order - it makes many sense.
There is a question for possible fix of my testCase: Is it possible to update manuully single Control/AppState after the rootNode update? Or change a separete Control/AppState updateOrder?

This order is part of the Application update (Going on memory here)… Guess you could extend Application @Override the update method,

But! Question arises quickly… if you are having to do this, perhaps consider that your design is flawed?

Um… that’s assuming update is protected. I do not remember if this is the case. Either way… point is:

Needing to alter the order in which a managed scene graphs is managed = design flaw or wrong choice or engines.

@t0neg0d that’s the issue. I cannot force my custom Control/AppState to be updated after the rooNode.

@pspeed then i have a feature request:
The ability to put a separete Control/AppState to be updated after rootNode update.
Is it possible to add? :slight_smile:

@t0neg0d said: if you are having to do this, perhaps consider that your design is flawed?

Is my testCase flawed? Or you can do it in a different way?

<cite>@mifth said:</cite> Is my testCase flawed? Or you can do it in a different way?

Honestly, I’m not sure. But I have found that if I am trying to do something in JME and it is working against the premise of the engine… then, chances are, I picked the wrong mechanism to leverage.

You say this works in a Control… but not an AppState?

Then wrap the Control in an AppState (if you need the functionality of AppState as well)

@t0neg0d said: Honestly, I'm not sure. But I have found that if I am trying to do something in JME and it is working against the premise of the engine... then, chances are, I picked the wrong mechanism to leverage.

You say this works in a Control… but not an AppState?

Then wrap the Control in an AppState (if you need the functionality of AppState as well)

it works only in one case: if a Control is attached to a child of rootNode. All other cases work with issues.

<cite>@mifth said:</cite> it works only in one case: if a Control is attached to a child of rootNode. All other cases work with issues.

I’ve never attached a control to the root node before. What was the reason for doing it this way? (Not saying you are wrong… just never thought of doing it /shrug). The reason I’ve never done it is, you can’t detach the root node to remove the control.

you can call someNode.updateLogicalState(tpf)

…but recognize that your controls will be called twice that frame.

Perhaps it’s better to rethink what you are trying to do and organize your stuff better to handle it. Or when two JME-provided things work counter to your goals then write your own class that combines them or something. Things like ChaseCamera are provided because they are really convenient but that doesn’t mean they are the best way.

for example, if your code was an app state and the chase camera logic was put into an app state… then you could easily control the order. Of course, you would also have to move the chased node as part of an app state… and so on.

Since I don’t even know the effect you are trying to achieve I can’t really comment.

A simpler design might be to figure out what “model” object (in the MVC sense of ‘model’) that these things are actually following and then update that in an app state and let everything else update to it.

Having one control depend on another control’s data elsewhere in the hierarchy is usually the sign of a serious design problem. I’d say most often it’s a sign that the developer is attempting to combine scene objects with game/model objects.

Aha, ok! Thank you a lot guys!
I’ll try to redesign my thing and give you to know i I get this ok.

EDITED: Generally, if i need to update a scene Spatial, so i need to update it from the scene(inside of rootNode). Now i understand how it works and will split LogicUpdate(States) and SceneUpdate(scene objects). Thanks!

1 Like