[SOLVED] Cant move the camera when rendering to texture

Is the process to render to texture done in another thread ?
I am trying to move the camera from an rendered texture, from another control, it simple dont move…
If I try to do the updateGeometricState or updateLogicalState it gets syncronized error or stackoverflow or do nothing as in the code / test case above :

TestRenderToTexture :

package mygame;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.LoopMode;
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.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
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;

public class TestRenderToTexture extends SimpleApplication {

    public Spatial offBox;
    private ViewPort offView;

    TestRenderToTextureMoveCam test;

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

    public Texture setupOffscreenView(){
        Camera offCamera = new Camera(512, 512);

        offView = renderManager.createPreView("Offscreen View", offCamera);
        offView.setClearFlags(true, true, true);
        offView.setBackgroundColor(ColorRGBA.DarkGray);

        // create offscreen framebuffer
        FrameBuffer offBuffer = new FrameBuffer(512, 512, 1);

        //setup framebuffer's cam
        offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f);
        offCamera.setLocation(new Vector3f(0f, 0f, -5f));
        offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);

        //setup framebuffer's texture
        Texture2D offTex = new Texture2D(512, 512, Format.RGBA8);
        offTex.setMinFilter(Texture.MinFilter.Trilinear);
        offTex.setMagFilter(Texture.MagFilter.Bilinear);

        //setup framebuffer to use texture
        offBuffer.setDepthBuffer(Format.Depth);
        offBuffer.setColorTexture(offTex);

        //set viewport to render to offscreen framebuffer
        offView.setOutputFrameBuffer(offBuffer);

        // setup framebuffer's scene
    offBox = assetManager.loadModel("Models/Sinbad/Sinbad.mesh.j3o");
    offBox.scale(0.3f);

        //Box boxMesh = new Box(Vector3f.ZERO, 1,1,1);
        //Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        //offBox = new Geometry("box", boxMesh);
        //offBox.setMaterial(material);

        // attach the scene to the viewport to be rendered
        offView.attachScene(offBox);

        return offTex;
    }

    @Override public void simpleInitApp() {
        cam.setLocation(new Vector3f(3, 3, 3));
        cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);

        //setup main scene
        Geometry quad = new Geometry("box", new Box(Vector3f.ZERO, 1,1,1));

        Texture offTex = setupOffscreenView();

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setTexture("ColorMap", offTex);
        quad.setMaterial(mat);
        rootNode.attachChild(quad);
    }

    public void moveCamera(float x, float y, float z) {
        offView.getCamera().getLocation().addLocal(new Vector3f(x,y,z));
    }

    @Override public void simpleUpdate(float tpf){
        if (offView.isEnabled()) {
            //offView.getCamera().setLocation(new Vector3f(offView.getCamera().getLocation().x+0.1f*tpf,offView.getCamera().getLocation().y,offView.getCamera().getLocation().z));
            offBox.updateLogicalState(tpf);
            offBox.updateGeometricState();

            if(test==null) test = new TestRenderToTextureMoveCam(this);
        }
    }

}

TestRenderToTextureMoveCam

package mygame;

import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control;
import java.io.IOException;

public class TestRenderToTextureMoveCam extends AbstractControl {

    TestRenderToTexture test;

    public TestRenderToTextureMoveCam(TestRenderToTexture test) {
        this.test = test;
        spatial = new Node();
        spatial.addControl(this);
    }

    @Override protected void controlUpdate(float tpf) {
        test.moveCamera(0.5f*tpf, 0, 0);
        test.offBox.updateGeometricState();
        test.offBox.updateLogicalState(tpf);
    }

    @Override protected void controlRender(RenderManager rm, ViewPort vp) { }

}

How to make the camera movement to work ?

You never attach this node to anything… therefore the node is never updated… therefore the control is never updated. Why do this in such a complicated Peter calls Paul which calls John that needs to call Peter to call Paul such a way?

In simpleUpdate() call move. See if that works.

Edit: and in general, have a control create its own spatial is extremely backwards. If you find yourself doing that then you are definitely doing it wrong… you are using a control for something it wasn’t meant in that case. (Controls are meant to be attached to spatials… they might create children but if they are also creating their own parent spatials then something is very wrong.)

Ops, I sorry, I forgot to add to the node…
It dosent make any diference thought …
Obs: Its just an test case, the real code on my game is different, I just built this fast test to show the problem. I hope its something I am doing wrong …

package mygame;

import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control;
import java.io.IOException;

public class TestRenderToTextureMoveCam extends AbstractControl {

    TestRenderToTexture test;

    public TestRenderToTextureMoveCam(Node onwernode, TestRenderToTexture test) {
        this.test = test;
        spatial = new Node();
        onwernode.attachChild(spatial);
        spatial.addControl(this);
    }

    @Override protected void controlUpdate(float tpf) {
        test.moveCamera(0.5f*tpf, 0, 0);
        test.offBox.updateGeometricState();
        test.offBox.updateLogicalState(tpf);
    }

    @Override protected void controlRender(RenderManager rm, ViewPort vp) { }

}

The main class :

package mygame;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.LoopMode;
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.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
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;

public class TestRenderToTexture extends SimpleApplication {

    public Spatial offBox;
    private ViewPort offView;

    TestRenderToTextureMoveCam test;

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

    public Texture setupOffscreenView(){
        Camera offCamera = new Camera(512, 512);

        offView = renderManager.createPreView("Offscreen View", offCamera);
        offView.setClearFlags(true, true, true);
        offView.setBackgroundColor(ColorRGBA.DarkGray);

        // create offscreen framebuffer
        FrameBuffer offBuffer = new FrameBuffer(512, 512, 1);

        //setup framebuffer's cam
        offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f);
        offCamera.setLocation(new Vector3f(0f, 0f, -5f));
        offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);

        //setup framebuffer's texture
        Texture2D offTex = new Texture2D(512, 512, Format.RGBA8);
        offTex.setMinFilter(Texture.MinFilter.Trilinear);
        offTex.setMagFilter(Texture.MagFilter.Bilinear);

        //setup framebuffer to use texture
        offBuffer.setDepthBuffer(Format.Depth);
        offBuffer.setColorTexture(offTex);

        //set viewport to render to offscreen framebuffer
        offView.setOutputFrameBuffer(offBuffer);

        // setup framebuffer's scene
        offBox = assetManager.loadModel("Models/Sinbad/Sinbad.mesh.j3o");
        offBox.scale(0.3f);
        //offBox = assetManager.loadModel("Models/evechar.j3o");
        //offBox.scale(0.5f); offBox.setLocalTranslation(0, -1, 0); offBox.rotate(0, 180*FastMath.DEG_TO_RAD, 0);
        //AnimChannel player_anim_channel;
        //AnimControl player_anim_control;
        //player_anim_control = Utils.getAnimControl( (Node)offBox );
        //if(player_anim_control!=null) {
        //    player_anim_channel = player_anim_control.createChannel();
        //    player_anim_channel.setAnim("Running"); player_anim_channel.setLoopMode(LoopMode.Loop);
        //}

        //Box boxMesh = new Box(Vector3f.ZERO, 1,1,1);
        //Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        //offBox = new Geometry("box", boxMesh);
        //offBox.setMaterial(material);

        // attach the scene to the viewport to be rendered
        offView.attachScene(offBox);

        return offTex;
    }

    @Override public void simpleInitApp() {
        cam.setLocation(new Vector3f(3, 3, 3));
        cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);

        //setup main scene
        Geometry quad = new Geometry("box", new Box(Vector3f.ZERO, 1,1,1));

        Texture offTex = setupOffscreenView();

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setTexture("ColorMap", offTex);
        quad.setMaterial(mat);
        rootNode.attachChild(quad);
    }

    public void moveCamera(float x, float y, float z) {
        offView.getCamera().getLocation().addLocal(new Vector3f(x,y,z));
    }

    @Override public void simpleUpdate(float tpf){
        if (offView.isEnabled()) {
            //offView.getCamera().setLocation(new Vector3f(offView.getCamera().getLocation().x+0.1f*tpf,offView.getCamera().getLocation().y,offView.getCamera().getLocation().z));
            offBox.updateLogicalState(tpf);
            offBox.updateGeometricState();

            if(test==null) test = new TestRenderToTextureMoveCam(rootNode, this);
        }
    }

}

About why I need it :

My game has two screen, one background screen that contains an big maze, and another screen at the front that is something like an len of the first screen, it zooms the first screen on it.
When the player moves, my plan is to move the lens quad on the first screen, and move the camera inside the second screen. If I just move the objects in the second screen it works, but when I try to move the camera it for some reason dont…
I could fix this moving all the word ( all objects ) in the oposit direction of the direction I want the camera to move, but this is bad and will required a lot of code, best would be to move the camera, and I have no idea why this camera is not moving…
Edit: Anyway, just to make it clear, I want the control of the spatial, to make the camera moves with him, and since the camera is an texture rendered one, its not working…

I don’t really care why you need it… you were doing it the WRONG way. If you ever ever ever find your yourself creating the control’s own spatial inside the constructor, take a step back, slap yourself fifty times, and try again. :wink:

If you want to have the camera follow a node… then attach the control to THAT NODE… and give the control the proper camera to move.

FOR TESTING PURPOSES: try removing this:
if(test==null) test = new TestRenderToTextureMoveCam(rootNode, this);

And replacing it with a call to moveCamera().

Yes, I realize that it is not the way you want to do it. IT IS FOR TESTING. SO TEST IT. SEE WHAT HAPPENS.

…so now I’ve said it twice and that’s it. No time for three times today.

Ok, I am missing something here…

  1. I cant attach the camera to any node because its an render to texture case, if I do it, the scene will not render at all…
  2. No need to test it, I already did and it works, just uncoment the line //offview.getcamera… , for some reason, if this is done in the same render update it works, if its on another update loop it dont, for me it looks like very odd, like if this is rendered in another thread or something…
    Obs: I just built the test case this way to make the TestRenderToTextureMoveCam as close to the original as possible… I could not find an bether and easy way to show this problem…

This is all so much meaningless garbage. You’d get identical results just adding the control to ownerNode.

Yes… but that line is BEFORE you update the state of offbox. The controls won’t be updated until much later.

Controls are generally a bad way to manage cameras because of when they get updated

No, there is only one thread. If you’d looked in a debugger or added printlns or any other standard way to debug stuff then you’d see the order that things are called.

You need to have your control updated as part of the offscreen render and not the main screen render, I guess.

And never ever ever ever never never never never ever never never never modify the results of getXXX() calls directly and expect them to mean anything. You MUST call the set. Always always always always always always. Did I say “always”? Always.

…else the camera won’t know that it has been moved. Don’t do this for camera, don’t do it for spatial, for best practices never do it for anything. If there is a set version of the get method then call it.

related to when you’d like them to get updated.

For something like this you will carefully need to manage when you move things and when you render things. Else things will get out of sync.

As part of an app state or at the beginning of simple update… decide where you want your box and your camera to be for that frame. The preview render will then update the camera (from a control) and the regular render will update the box (from a control). That should keep everyone updating the right way.

This tendency to treat cameras and nodes as game objects gets so many people in trouble over and over.

You need to have your control updated as part of the offscreen render and not the main screen render, I guess.

You were right ! :smile:

I added this code to force the camera to just move on the right moment :

public void moveCamera(float x, float y, float z) {
    cameraNewPos = offView.getCamera().getLocation();
    cameraNewPos.addLocal( new Vector3f(x,y,z) );
    movecameraon=true;
}

On the update before the update logical state :

        if(movecameraon) {
             offView.getCamera().setLocation(cameraNewPos);
            movecameraon = false;
        }

Its working now !!!

You were right… I guesses and always used the camera as an object, but its part of the renderer, so we need to take care on the renderer order when change it…

This part of the OP’s use case is interesting to me. He has two views of a single scene, and in this case one view (the “magnified” view) is overlayed on top of the other.

Is there a standard/recommended way to do this? Maybe a tutorial or something, I wouldn’t even know what to look for in the existing tutorials. What are multiple views called?

(Think about this, I did a search for “split screen” and I see there’s a tutorial on JME how to do that. I haven’t read it yet, but would you use that as a base, or is overlaying two views completely different?)

Start up with the TestRenderToTexture example from the test classes to understand the basics.
Also take a look on my last 3 topics, there is a lot of tips on they ( including this one ).

I think it could also be done with viewports… minimaps and stuff are done this way and it’s a similar thing. For quads on screen I think the viewport approach might be easier than rendering to texture.

Thanks! Rendering to a texture was something I didn’t know about. Viewports appear in the tutorial I found, and it looks like a fairly complete solution, so thanks for confirming it as something worth while.

Yeah, as soon as it becomes something other than a rectangle / square you will need render to texture - or a special shader that alpha-multiplies with a mask or radius (if circular). Also, at the edges you could give it some nice fish-eye effect via shaders (similar to post effect filters). You could also combine both: render to texture first, then use a shader for the fish-eye-distortion / circular alpha fading. :chimpanzee_smile: