AbstractAppState initialize different to SimpleApplication simpleInitApp

in my subclass of SimpleApplication i do the following which works fine:

[java]
public void simpleInitApp() {
// TODO move to AppState:
cam.setFrustumPerspective(CAMERA_VIEW_ANGLE, cam.getWidth() / cam.getHeight(), MIN_VIEW_DISTANCE, MAX_VIEW_DISTANCE);

[/java]

When i try to do the same in the initialize method of my appstate, it does not work correctly. the aspect ration seems to be calculated wrong

There is no reason for this to happen unless you are somehow getting the wrong camera. How does your app state get access to “cam”?

It is strange to show the working code and not the non-working code. :slight_smile:

1 Like

ok you are right, here is the AppState code:

[java]public void initialize(AppStateManager stateManager, Application a) {
super.initialize(stateManager, a);
this.app = (SimpleApplication) a;
Camera cam = app.getCamera();
cam.setFrustumPerspective(CAMERA_VIEW_ANGLE,
cam.getWidth() / cam.getHeight(), MIN_VIEW_DISTANCE,
MAX_VIEW_DISTANCE);
}[/java]

To initialize the app state i add it like this (I am working with Android and i do this from the ui thread so not in the rendering loop, maybe thats a problem?):

[java]MyAppState myAppStateController = new MyAppState();
app.getStateManager().attach(arAppStateController);[/java]

initialize() is always called on the render thread by the AppStateManager.

When do you attach the app state? Maybe it’s not about simpleInit() version AppState.initialize() but about when in the app’s life cycle the camera is changed.

It shouldn’t matter but I have no familiarity with Android, so I can’t say for sure that setting the camera later doesn’t cause some issues.

Also, I’m making assumptions that this is a SImpleApplication subclass and that the app state manager is being properly managed (SimpleApplication does that automatically but regular Application does not).

jep i know that initialize is called from the render thread, i meant that i add the AppState from the Android UI thread.
I’m using a normal SImpleApplication without any modifications. I debugged the code and the values which are passed to the setFrustumPerspective method are identical, so the camera has the correct height and width mit does not like to be modified after it is already used for rendering, i think it must be something like this. Do I have to call any refresh or update method after i modify it? what happens to the camera between the SimpleApplication.simpleInitApp and the AbstractAppState.initialize call?

@simon.heinen said: jep i know that initialize is called from the render thread, i meant that i add the AppState from the Android UI thread. I'm using a normal SImpleApplication without any modifications. I debugged the code and the values which are passed to the setFrustumPerspective method are identical, so the camera has the correct height and width mit does not like to be modified after it is already used for rendering, i think it must be something like this. Do I have to call any refresh or update method after i modify it? what happens to the camera between the SimpleApplication.simpleInitApp and the AbstractAppState.initialize call?

Nothing.

simpleInit() is called then initialize() on the app states is called. Unless you have some other app state messing with the camera code then it should be fine.

Maybe you can try a simple test case on desktop JME to see if this is an android specific problem. I’ve changed camera settings before while the app is running without issue. So something else may be at play. A simple test case will rule that out also.

On Android, I usually change the camera settings in the reshape method of the app.

[java]
@Override
public void reshape(int w, int h){
logger.log(Level.INFO, “reshape{w:{0}, h:{1}”,
new Object[]{w, h});
super.reshape(w, h);
// put code here to resize and reshape ui objects and change camera frustrum based on new GLSurfaceView size
}
[/java]

The reshape of the app is called whenever the GLSurfaceView changes size which includes when the game start up as well as if you allow changes to the orientation based on the Android orientation of the device.

@pspeed said:
@simon.heinen said: jep i know that initialize is called from the render thread, i meant that i add the AppState from the Android UI thread. I'm using a normal SImpleApplication without any modifications. I debugged the code and the values which are passed to the setFrustumPerspective method are identical, so the camera has the correct height and width mit does not like to be modified after it is already used for rendering, i think it must be something like this. Do I have to call any refresh or update method after i modify it? what happens to the camera between the SimpleApplication.simpleInitApp and the AbstractAppState.initialize call?

Nothing.

simpleInit() is called then initialize() on the app states is called. Unless you have some other app state messing with the camera code then it should be fine.

Maybe you can try a simple test case on desktop JME to see if this is an android specific problem. I’ve changed camera settings before while the app is running without issue. So something else may be at play. A simple test case will rule that out also.

ok i testet it, on the desktop there is no strange behaviour, i will try the resize method concept, but i think this is a jme android bug then which should somehow be fixed?

I wonder if the reshape is getting called internally after simpleInitApp which is then resetting the camera frustrum somehow after you set it.

ah, actually, I remember now that I used to have to set the position of UI objects during the first simpleUpdate because of something similar to this. I think I remember that the screen size changed after simpleInitApp. If that is true, then it would make sense that jME is setting the camera after you are changing it in simpleInitApp.

Let me know if overriding the reshape or putting the code in the first simpleUpdate call helps. That will be the clue.

So, for the record, I forgot that the app state initialize() is called during Application.update(). It’s called the first thing in update().

So whatever is done in the render thread on Android between calling initialize() and update() is the culprit. And I think you should totally be able to change a camera’s settings at runtime so that would be a bug. Changing field of view, etc. is a very common thing to want to do.

even if i change the camera in the update method of the AppState this behavior happens:

[java] public void update(float tpf) {
super.update(tpf);
if (firstTime) {
firstTime = false;
Camera cam = app.getCamera();
cam.setFrustumPerspective(CAMERA_VIEW_ANGLE,
cam.getWidth() / cam.getHeight(), MIN_VIEW_DISTANCE,
MAX_VIEW_DISTANCE);
}
…[/java]

when i do the same in the SimpleApplication like this:

[java] public void simpleUpdate(float tpf) {
super.simpleUpdate(tpf);
if (firstTime) {
firstTime = false;
cam.setFrustumPerspective(CAMERA_VIEW_ANGLE,
cam.getWidth() / cam.getHeight(), MIN_VIEW_DISTANCE,
MAX_VIEW_DISTANCE);
}
}[/java]

the same problem appears. So the camera cant be changed after the simpleInitApp() has finished without causing this bug

@simon.heinen said: the same problem appears. So the camera cant be changed after the simpleInitApp() has finished without causing this bug

Ok, sounds like there is a strange bug in the android layer somehow. I guess it does not pick up the new camera frustum settings.

@pspeed said:
@simon.heinen said: the same problem appears. So the camera cant be changed after the simpleInitApp() has finished without causing this bug

Ok, sounds like there is a strange bug in the android layer somehow. I guess it does not pick up the new camera frustum settings.

so should i report this bug somewhere? or move this topic in another forum?

I’m not seeing anything Android specific that would cause this yet. I looked at the process flow again and it looks like it is the following:

Android SurfaceView is created which calls app.initialize / simpleInitApp
Android SurfaceView surface size is reported which calls app.reshape based on new surface view size
Android GLThread update loop starts that calls app.update in jME (repeats obviously until game exits)

By default, RenderManager calls cam.resize(w, h, true); when a reshape is done which may modify the frustum, but this is done before the app state is initialized.

The internal call to resize the camera happens after simpleInitApp, but before the first update loop that initializes appstates. This shouldn’t be an issues to set the camera frustum during or after the initialize of the appstate, just maybe not during simpleInitApp.

Is there anything else Android specific going on, like not running full screen on the device (ie. showing the notification bar on the device) or changing the size / layout of the GLSurfaceView after the initial onCreate?

I just ran the test case which attaches an appstate during simpleInitApp and then changes the camera Frustum during the initialize of the appstate. Everything worked fine. Can you run the test case below and see if you also have the same results? I changed the CAMERA_VIEW_ANGLE variable in the appstate at the bottom between 45f and 15f and there is a definite difference in how much is viewed on the screen.

[EDIT] The texture is from the test-data jar file. Make sure to include the test-data library to get it to run.

[java]
package mygame;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.TextureKey;
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.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.texture.Texture;

/**

  • test

  • @author normenhansen
    */
    public class Main extends SimpleApplication {
    private Geometry geom1;
    private TextureKey texKey1;
    private Texture tex1;
    private Material mat1;
    private Geometry geom2;
    private TextureKey texKey2;
    private Texture tex2;
    private Material mat2;
    private Geometry geom3;
    private TextureKey texKey3;
    private Texture tex3;
    private Material mat3;
    private Geometry geom4;
    private TextureKey texKey4;
    private Texture tex4;
    private Material mat4;

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

    @Override
    public void simpleInitApp() {
    flyCam.setDragToRotate(true);

     MyAppState myAppStateController = new MyAppState();
     getStateManager().attach(myAppStateController);
    
     Box b1 = new Box(Vector3f.ZERO, 1, 1, 1);
     geom1 = new Geometry("Box", b1);
     texKey1 = new TextureKey("Textures/Sky/Lagoon/lagoon_west.jpg", true);
     tex1 = assetManager.loadTexture(texKey1);
     mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
     mat1.setColor("Color", ColorRGBA.Blue);
     mat1.setTexture("ColorMap", tex1);
     geom1.setMaterial(mat1);
     geom1.setLocalTranslation(-2f, -2f, 0f);
     rootNode.attachChild(geom1);
    
     Box b2 = new Box(Vector3f.ZERO, 1, 1, 1);
     geom2 = new Geometry("Box", b2);
     texKey2 = new TextureKey("Textures/Sky/Lagoon/lagoon_east.jpg", true);
     tex2 = assetManager.loadTexture(texKey2);
     mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
     mat2.setColor("Color", ColorRGBA.Blue);
     mat2.setTexture("ColorMap", tex2);
     geom2.setMaterial(mat2);
     geom2.setLocalTranslation(2f, -2f, 0f);
     rootNode.attachChild(geom2);
    
     Box b3 = new Box(Vector3f.ZERO, 1, 1, 1);
     geom3 = new Geometry("Box", b3);
     texKey3 = new TextureKey("Textures/Sky/Lagoon/lagoon_north.jpg", true);
     tex3 = assetManager.loadTexture(texKey3);
     mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
     mat3.setColor("Color", ColorRGBA.Blue);
     mat3.setTexture("ColorMap", tex3);
     geom3.setMaterial(mat3);
     geom3.setLocalTranslation(-2f, 2f, 0f);
     rootNode.attachChild(geom3);
    
     Box b4 = new Box(Vector3f.ZERO, 1, 1, 1);
     geom4 = new Geometry("Box", b4);
     texKey4 = new TextureKey("Textures/Sky/Lagoon/lagoon_south.jpg", true);
     tex4 = assetManager.loadTexture(texKey4);
     mat4 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
     mat4.setColor("Color", ColorRGBA.Blue);
     mat4.setTexture("ColorMap", tex4);
     geom4.setMaterial(mat4);
     geom4.setLocalTranslation(2f, 2f, 0f);
     rootNode.attachChild(geom4);
    

    }

    @Override
    public void simpleUpdate(float tpf) {
    //TODO: add update code
    }

    @Override
    public void simpleRender(RenderManager rm) {
    //TODO: add render code
    }

    private class MyAppState extends AbstractAppState {
    SimpleApplication app;
    float CAMERA_VIEW_ANGLE = 45f;
    float MIN_VIEW_DISTANCE = 1f;
    float MAX_VIEW_DISTANCE = 100f;

     @Override
     public void initialize(AppStateManager stateManager, Application a) {
         super.initialize(stateManager, a);
         this.app = (SimpleApplication) a;
         Camera cam = app.getCamera();
         cam.setFrustumPerspective(CAMERA_VIEW_ANGLE,
                 cam.getWidth() / cam.getHeight(), MIN_VIEW_DISTANCE,
                 MAX_VIEW_DISTANCE);
     }
    

    }
    }
    [/java]

Ok i tried it on a Android 3.0 tablet and an android 4 galaxy s3 and on both the same problem. It looks like the glsurfaceview is streched incorrect when i rotate the camera arount its x axis you can see it (maybe you didnt rotate the device around this axis and didnt notice it?). i just noticed one more thing. when you turn of the device screen and trun it on again, the proportions are correct, so after the recreate process it works fine.

Try changing the following line:
[java]
cam.setFrustumPerspective(CAMERA_VIEW_ANGLE,
cam.getWidth() / cam.getHeight(), MIN_VIEW_DISTANCE,
MAX_VIEW_DISTANCE);
[/java]
to:
[java]
cam.setFrustumPerspective(CAMERA_VIEW_ANGLE,
((float)cam.getWidth() / cam.getHeight()), MIN_VIEW_DISTANCE,
MAX_VIEW_DISTANCE);
[/java]

If you are starting in Portrait mode, the width is smaller than the height. cam.getWidth() and cam.getHeight() are both int type so cam.getWidth() / cam.getHeight() is 0 unless you cast to float. This is setting your left and right frustum to 0 instead of the correct values.

2 Likes
@iwgeric said: Try changing the following line: [java] cam.setFrustumPerspective(CAMERA_VIEW_ANGLE, cam.getWidth() / cam.getHeight(), MIN_VIEW_DISTANCE, MAX_VIEW_DISTANCE); [/java] to: [java] cam.setFrustumPerspective(CAMERA_VIEW_ANGLE, ((float)cam.getWidth() / cam.getHeight()), MIN_VIEW_DISTANCE, MAX_VIEW_DISTANCE); [/java]

If you are starting in Portrait mode, the width is smaller than the height. cam.getWidth() and cam.getHeight() are both int type so cam.getWidth() / cam.getHeight() is 0 unless you cast to float. This is setting your left and right frustum to 0 instead of the correct values.

oh no i feel so stupid :smiley: thanks alot, jep it was that simple