Search code examples
javalibgdx

LIBGDX: How do you horizontally/vertically flip a camera view?


I am trying to create a four way symmetric program that has 1 camera viewing a specific scene, but just flipping the view of the camera for each of the 4 panels.

For example: symmetry

Quadrant 1 would be the regular orientation of the world with positive x to the right and positive y upwards. Quadrant 2 would be positive x to the left and positive y upwards, and so on.

I've figured out a way to draw the same camera view in multiple panels by starting and ending my spritebatch multiple times (not sure if this is a bad thing but it's the way I got it to work) and changing the glViewport of each panel.

    batch.setProjectionMatrix(cam.combined);

    batch.begin();        
    Gdx.gl.glViewport(0,Gdx.graphics.getHeight()/2,Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2);
    batch.draw(img, 0, 0);
    batch.end();

    batch.begin();
    Gdx.gl.glViewport(0,0,Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2);
    batch.draw(img, 0, 0);
    batch.end();        

    batch.begin();
    Gdx.gl.glViewport(Gdx.graphics.getWidth()/2,0,Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2);
    batch.draw(img, 0, 0);
    batch.end();

    batch.begin();
    Gdx.gl.glViewport(Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2,Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2);
    batch.draw(img, 0, 0);
    batch.end();

I think a linear transformation would do it but I haven't worked with libgdx or linear algebra enough to see an immediate clear answer.

Any help would be appreciated. Thanks.


Solution

  • There are a couple ways you can do this.

    Flipping the camera

    Keep a matrix around for doing the flips without messing with the camera.

    private final Matrix4 tmpM = new Matrix4();
    

    Then you can flip it for the three flipped quadrants:

    Gdx.gl.glViewport(0,Gdx.graphics.getHeight()/2,Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2);
    batch.setProjectionMatrix(cam.combined);
    batch.begin();        
    batch.draw(img, 0, 0);
    batch.end();
    
    Gdx.gl.glViewport(0,0,Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2);
    batch.setProjectionMatrix(tmpM.set(cam.combined).scl(1, -1, 1));
    batch.begin();        
    batch.draw(img, 0, 0);
    batch.end();
    
    Gdx.gl.glViewport(Gdx.graphics.getWidth()/2,0,Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2);
    batch.setProjectionMatrix(tmpM.set(cam.combined).scl(-1, -1, 1));
    batch.begin();        
    batch.draw(img, 0, 0);
    batch.end();
    
    Gdx.gl.glViewport(Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2,Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight()/2);
    batch.setProjectionMatrix(tmpM.set(cam.combined).scl(-1, -1, 1));
    batch.begin();        
    batch.draw(img, 0, 0);
    batch.end();
    

    FrameBuffer

    If your scene is complex, this may be faster because the actual scene is drawn only one time. Create your frame buffer object in resize. The try/catch is because some old or budget phones don't support RGBA8888 color in a frame buffer object.

    public void resize (int width, int height){
        if (frameBuffer != null) frameBuffer.dispose();
        try {
            frameBuffer = new FrameBuffer(Pixmap.Format.RGBA8888, width/2, height/2, false); 
        } catch (GdxRuntimeException e) {
            frameBuffer = new FrameBuffer(Pixmap.Format.RGB565, width/2, height/2, false);
        }
    }
    

    Then use it as follows:

    frameBuffer.begin();
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    batch.setProjectionMatrix(cam.combined);
    batch.begin();        
    batch.draw(img, 0, 0);
    batch.end();
    frameBuffer.end();
    
    Texture fbTex = frameBuffer.getColorBufferTexture();
    batch.getProjectionMatrix().idt();
    batch.begin();
    batch.draw(fbTex, -1, 1, 1, -1);
    batch.draw(fbTex, -1, 0, 1, 1);
    batch.draw(fbTex, 0, 0, 1, 1);
    batch.draw(fbTex, 0, 1, 1, -1);
    batch.end();