Textures disappear when using render to texture in a canvas

Hi,



I'm using jME 2.0 for two months now and I finally came to a point where I need some help. I have a JFrame with a jME canvas (similar to the JMESwingTest.java). The canvas shows a scene with textured houses, a skybox and some transparency effects. I now wanted to add a minimap, using render to texture. But it seems as if the renderers are not able to share the textures: the textures seen in the minimap are replaced in the main view by other textures which are not visible in the minimap :?.



To make it more clear, I made two test classes: one uses a canvas inside a JFrame, while the other extends SimpleGame. There is a simple scene containing a textured box and a quad to which the texture from the TextureRenderer is applied. Everything looks good when I use the class that extends SimpleGame. But when I use the class with the canvas inside the JFrame, the texture on the box gets lost when I use the TextureRenderer.render() method. I only see "Texture Missing" on the box, while the texture in the minimap looks fine. (Vice versa, if I call the TextureRenderer.render() method not right at the beginning but after some seconds, the texture on the box is correct, but the minimap doesn't show the texture.) Both classes have identical code in the simpleRender() and simpleSetup()/simpleInitGame() methods.



Here are the two test classes. Just replace the picture used for the texture to test them.

First the Class with the canvas:


import javax.swing.JFrame;
import com.jme.image.Texture;
import com.jme.image.Texture2D;
import com.jme.input.InputHandler;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.TextureRenderer;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.canvas.SimpleCanvasImpl;
import com.jme.system.lwjgl.LWJGLSystemProvider;
import com.jme.util.TextureManager;
import com.jmex.awt.lwjgl.LWJGLAWTCanvasConstructor;
import com.jmex.awt.lwjgl.LWJGLCanvas;

public class TestRTTCanvas extends SimpleCanvasImpl {

    private DisplaySystem display;
    private LWJGLCanvas canvas = null;
    private static int canvasWidth = 640;
    private static int canvasHeight = 480;
    private InputHandler input;
   
    private Texture tex1 = TextureManager.loadTexture(
            this.getClass().getClassLoader().getResource(
            "main/testpic.gif" ),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear );
   
    private Quad q;
    private TextureRenderer tRenderer;
    private Texture2D mapTexture;
    private TextureState texState;
   
    public static void main(String[] args) {
        TestRTTCanvas test = new TestRTTCanvas( canvasWidth, canvasHeight );
    }
   
    private TestRTTCanvas(int width, int height) {
        super( width, height );
        display = DisplaySystem.getDisplaySystem( LWJGLSystemProvider.LWJGL_SYSTEM_IDENTIFIER );
        display.registerCanvasConstructor( "AWT", LWJGLAWTCanvasConstructor.class );
        canvas = (LWJGLCanvas)display.createCanvas( canvasWidth, canvasHeight );
        canvas.setImplementor( this );
        canvas.setBounds( 0, 0, canvasWidth, canvasHeight );
       
        JFrame frame = new JFrame();
        frame.getContentPane().add( canvas );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setVisible( true );
    }
   
    @Override
    public void simpleRender() {
        display.getRenderer().draw( q );
    }
   
    @Override
    public void simpleSetup() {
             
        input = new InputHandler();
        cam.setLocation( new Vector3f( 2.0f, 0.0f, 12.0f ));
       
        //Setup a simple scene (textured box)
        Box box = new Box( "box", new Vector3f( 0, 0, 0 ), 1.0f, 1.0f, 1.0f );
        box.setLocalTranslation( 5.0f, 0.0f, 0.0f );
       
        TextureState ts = display.getRenderer().createTextureState();
        ts.setTexture( tex1 );
        ts.setEnabled( true );
        box.setRenderState( ts );
        box.updateRenderState();
        rootNode.attachChild( box );
       
        //Setup the quad to which the texture gets applied
        q = new Quad( "Quad", 7.0f, 7.0f );
        q.setCullHint( Spatial.CullHint.Never );
        q.setLightCombineMode( Spatial.LightCombineMode.Off );
        q.setRenderQueueMode( Renderer.QUEUE_ORTHO );
        ZBufferState buf = DisplaySystem.getDisplaySystem().getRenderer().createZBufferState();
        buf.setEnabled( true );
        buf.setFunction( ZBufferState.TestFunction.LessThanOrEqualTo );
        q.setRenderState( buf );
       
        //Setup the texture, the texture state and the texture renderer
        mapTexture = new Texture2D();
        mapTexture.setWrap( Texture.WrapMode.Clamp );
        mapTexture.setRenderToTextureType( Texture.RenderToTextureType.RGBA );
       
        tRenderer = DisplaySystem.getDisplaySystem().createTextureRenderer( 512, 512,
                        TextureRenderer.Target.Texture2D );
        tRenderer.setBackgroundColor( new ColorRGBA( 0.5f, 0.5f, 0.5f, 1.0f ));
        if ( tRenderer.isSupported() ) {
            tRenderer.setupTexture( mapTexture );
            tRenderer.getCamera().setLocation( new Vector3f( 5.0f, 1.0f, 5.0f ));
            tRenderer.getCamera().update();
        } else {
            System.err.println( "Render to texture not supported!" );
        }
       
        texState = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        texState.setTexture( mapTexture );
        texState.setEnabled( true );
       
        //Apply texture state to the quad
        q.setRenderState( texState );
        q.updateRenderState();
    
        rootNode.updateRenderState();
       
        //Render to texture
        tRenderer.render( rootNode, mapTexture );
    }
}



And here the class extending SimpleGame:

import com.jme.app.SimpleGame;
import com.jme.image.Texture;
import com.jme.image.Texture2D;
import com.jme.input.InputHandler;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.TextureRenderer;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;

public class TestRTTSimpleGame extends SimpleGame {

    private Texture tex1 = TextureManager.loadTexture(
            this.getClass().getClassLoader().getResource(
            "main/testpic.gif" ),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear );
   
    private Quad q;
    private TextureRenderer tRenderer;
    private Texture2D mapTexture;
    private TextureState texState;
   
    public static void main(String[] args) {
        TestRTTSimpleGame app = new TestRTTSimpleGame();
        app.setConfigShowMode(ConfigShowMode.AlwaysShow);
        app.start();
    }
   
    @Override
    protected void cleanup() {
        super.cleanup();
        tRenderer.cleanup();
    }

    @Override
    protected void simpleRender() {
        display.getRenderer().draw( q );
    }
   
    @Override
    protected void simpleInitGame() {
            
        input = new InputHandler();
        cam.setLocation( new Vector3f( 2.0f, 0.0f, 12.0f ));
       
        //Setup a simple scene (textured box)
        Box box = new Box( "box", new Vector3f( 0, 0, 0 ), 1.0f, 1.0f, 1.0f );
        box.setLocalTranslation( 5.0f, 0.0f, 0.0f );
       
        TextureState ts = display.getRenderer().createTextureState();
        ts.setTexture( tex1 );
        ts.setEnabled( true );
        box.setRenderState( ts );
        box.updateRenderState();
        rootNode.attachChild( box );
       
        //Setup the quad to which the texture gets applied
        q = new Quad( "Quad", 7.0f, 7.0f );
        q.setCullHint( Spatial.CullHint.Never );
        q.setLightCombineMode( Spatial.LightCombineMode.Off );
        q.setRenderQueueMode( Renderer.QUEUE_ORTHO );
        ZBufferState buf = DisplaySystem.getDisplaySystem().getRenderer().createZBufferState();
        buf.setEnabled( true );
        buf.setFunction( ZBufferState.TestFunction.LessThanOrEqualTo );
        q.setRenderState( buf );
        //q.setLocalTranslation( -10.0f, -5.0f, 0.0f );
        //rootNode.attachChild( q );
       
        //Setup the texture, the texture state and the texture renderer
        mapTexture = new Texture2D();
        mapTexture.setWrap( Texture.WrapMode.Clamp );
        mapTexture.setRenderToTextureType( Texture.RenderToTextureType.RGBA );
       
        tRenderer = DisplaySystem.getDisplaySystem().createTextureRenderer( 512, 512,
                        TextureRenderer.Target.Texture2D );
        tRenderer.setBackgroundColor( new ColorRGBA( 0.5f, 0.5f, 0.5f, 1.0f ));
        if ( tRenderer.isSupported() ) {
            tRenderer.setupTexture( mapTexture );
            tRenderer.getCamera().setLocation( new Vector3f( 5.0f, 1.0f, 5.0f ));
            tRenderer.getCamera().update();
        } else {
            System.err.println( "Render to texture not supported!" );
        }
       
        texState = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        texState.setTexture( mapTexture );
        texState.setEnabled( true );
       
        //Apply texture state to the quad
        q.setRenderState( texState );
        q.updateRenderState();
       
        rootNode.updateRenderState();
       
        //Render to texture
        tRenderer.render( rootNode, mapTexture );
    }
}



And here the results (left the class with the canvas, right the class extending SimpleGame):



Also, my notebook's graphic card doesn't seem to support the FBO and uses the PBuffer for the RTT (if this plays an important role?). By the way, all the RTT tests inside the jME project are running fine, but they all extend some kind of Game class.

Has anyone an idea how to fix my problem when using RTT and a canvas inside a JFrame?
Maybe I need to add something to the SceneImplementor that is otherwise already done by SimpleGame? But then, I already looked at the SimpleGame classes and couldn't find anything.


EDIT: I just noticed that the canvas program freezes when I press the arrow keys or the alt key. I commented out line by line until I found the line which is causing this problem:

tRenderer = display.createTextureRenderer( 512, 512,
                                  TextureRenderer.Target.Texture2D );


Maybe this gives another hint on what is wrong with my canvas program?

i can reproduce your problem, if i force Pbuffer, it works fine when using FBO.



… but i think the real problem ist the order of setting and updateing the renderstates and textures

Thanks for testing it, Core-Dump!

I played around with the order of the settings and updating as you suggested, but had no luck.



I made a new test with a very simple scene consisting of an untextured, rotating box (no textures or renderstates except the ones in SimpleCanvasImpl). This test still freezes when I put focus to the canvas by clicking with the mouse and then press an arrow key or the alt key (first the window itself freezes for some seconds, if I try to move the window after that, the box stops turning. Also, the logger confirming the key presses stops to output to the console). If you want to test it, you will have to force PBuffer again. Here is the code:

import java.awt.BorderLayout;
import java.awt.event.KeyListener;
import java.util.logging.Logger;
import javax.swing.JFrame;
import com.jme.bounding.BoundingSphere;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.TextureRenderer;
import com.jme.scene.shape.Box;
import com.jme.system.DisplaySystem;
import com.jme.system.canvas.SimpleCanvasImpl;
import com.jme.system.lwjgl.LWJGLSystemProvider;
import com.jmex.awt.input.AWTMouseInput;
import com.jmex.awt.lwjgl.LWJGLAWTCanvasConstructor;
import com.jmex.awt.lwjgl.LWJGLCanvas;

public class TestRTTCanvasSmall extends SimpleCanvasImpl {

    private static final Logger logger = Logger.getLogger(TestRTTCanvasSmall.class.getName());
    private DisplaySystem display;
    private LWJGLCanvas canvas = null;
    private static int canvasWidth = 640;
    private static int canvasHeight = 480;
    private TextureRenderer tRenderer;
    private InputHandler input;
   
    private Box box;
    private float angle = 0;
    private float angle2 = 0;
    private Quaternion rotQuat = new Quaternion();
    private Quaternion rotMBQuat = new Quaternion();
    private Vector3f axis = new Vector3f(1, 1, 0.5f);
   
    public static void main(String[] args) {
        new TestRTTCanvasSmall( canvasWidth, canvasHeight );
    }
   
    private TestRTTCanvasSmall(int width, int height) {
        super( width, height );
       
        display = DisplaySystem.getDisplaySystem( LWJGLSystemProvider.LWJGL_SYSTEM_IDENTIFIER );
        display.registerCanvasConstructor( "AWT", LWJGLAWTCanvasConstructor.class );
        canvas = (LWJGLCanvas)display.createCanvas( width, height );
       
        canvas.setUpdateInput(true);
        KeyInput.setProvider(KeyInput.INPUT_AWT);
        KeyListener kl = (KeyListener) KeyInput.get();
        canvas.addKeyListener(kl);
        AWTMouseInput.setup(canvas, false);
       
        canvas.setImplementor( this );
        canvas.setBounds( 0, 0, canvasWidth, canvasHeight );
       
        JFrame frame = new JFrame();
        frame.getContentPane().add( canvas, BorderLayout.CENTER );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setVisible( true );
    }
   
    @Override
    public void simpleUpdate() {
          input.update(tpf);
       
          //Rotate the box
          if (tpf < 1) {
            angle = angle + (tpf * -.25f);
            angle2 = angle2 + (tpf * 1);
            if (angle < 0) {
              angle = 360 - .25f;
            }
            if (angle2 >= 360) {
              angle2 = 0;
            }
          }
          rotQuat.fromAngleAxis(angle, axis);
          rotMBQuat.fromAngleAxis(angle2, axis);
          box.setLocalRotation(rotMBQuat);
    }
   
    @Override
    public void simpleSetup() {
       
        //Input
        input = new InputHandler();
        input.addAction(new InputAction() {
            public void performAction(InputActionEvent evt) {
                logger.info(evt.getTriggerName());
            }
        }, InputHandler.DEVICE_MOUSE, InputHandler.BUTTON_ALL,
                InputHandler.AXIS_NONE, false);
        input.addAction(new InputAction() {
            public void performAction(InputActionEvent evt) {
                logger.info(evt.getTriggerName());
            }
        }, InputHandler.DEVICE_KEYBOARD, InputHandler.BUTTON_ALL,
                InputHandler.AXIS_NONE, false);
       
        //Just a box
        box = new Box( "box", new Vector3f(-5, -5, -5), new Vector3f(5, 5, 5) );
        box.setModelBound(new BoundingSphere());
        box.updateModelBound();
        box.setLocalTranslation(new Vector3f(0, 0, 0));
        rootNode.attachChild( box );
       
        //This line causes problems for me
        tRenderer = display.createTextureRenderer( 512, 512,
                        TextureRenderer.Target.Texture2D );
    }
}



If I remove the createTextureRenderer() call, the freeze does not happen. I think this is really strange as I don't know how the PBuffer texture renderer could interfere with the user input :? (note: the freeze also happens when there is no keyboard action added to the inputHandler).

It seems there must be a solution, since the PBuffer RTT works with the SimpleGame test. But maybe I have to switch to a SimpleGame or BaseGame implementation. At least this way I'll get an easy fullscreen option for free :).

it does not freeze here

Hmm… so maybe it's an issue with my system / notebook drivers.

Thanks for the answer.


Hi Sammy and Core-Dump,



I am very happy to find your posts as I was going to become crazy. It is as you have found out: the textureToRender Stuff that is working perfectly with SimpleGame does not work at all with SimpleCanvasImpl. I have a card which does not  support FBO, though PBuffer buffer was selected instead.



Are there any news to this problem since May last year?



Bye elaspix