[SOLVED] aspect ratios of cameras in a split-screen render

I’m implementing split-screen rendering (two scenes displayed simultaneously) for Maud, and I hit a snag that seems related to camera aspect ratios. Perhaps someone here can help. Here’s an extremely simplified version of what I’m doing:

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Box;

public class Main extends SimpleApplication implements ActionListener {
    boolean splitScreen = false;
    Box mesh = new Box(1f, 1f, 1f);
    Camera leftCam, rightCam;
    Node leftScene = new Node("left scene");
    ViewPort leftView, rightView;

    @Override
    public void simpleInitApp() {
        flyCam.setEnabled(false);

        Geometry blueBox = new Geometry("blue box", mesh);
        Material blueMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        blueMat.setColor("Color", ColorRGBA.Blue);
        blueBox.setMaterial(blueMat);
        rootNode.attachChild(blueBox);

        rightCam = cam.clone();
        rightCam.setViewPort(0.5f, 1f, 0f, 1f);

        rightView = renderManager.createMainView("right", rightCam);
        rightView.setClearFlags(true, true, true);
        rightView.setEnabled(false);
        rightView.attachScene(rootNode);

        Geometry redBox = new Geometry("red box", mesh);
        Material redMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        redMat.setColor("Color", ColorRGBA.Red);
        redBox.setMaterial(redMat);
        leftScene.attachChild(redBox);

        leftCam = cam.clone();
        leftCam.setViewPort(0f, 0.5f, 0f, 1f);

        leftView = renderManager.createMainView("left", leftCam);
        leftView.setClearFlags(true, true, true);
        leftView.setEnabled(false);
        leftView.attachScene(leftScene);

        inputManager.addMapping("lmb", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
        inputManager.addListener(this, "lmb");
        inputManager.addMapping("rmb", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
        inputManager.addListener(this, "rmb");
    }

    @Override
    public void onAction(String name, boolean keyPressed, float tpf) {
        if (name.equals("lmb") && !keyPressed) {
            splitScreen = !splitScreen;
            viewPort.setEnabled(!splitScreen);
            leftView.setEnabled(splitScreen);
            rightView.setEnabled(splitScreen);
        } else if (name.equals("rmb") && !keyPressed) {
            // dump debugging info here
        }
    }

    @Override
    public void simpleUpdate(float tpf) {
        leftScene.updateLogicalState(tpf);
        leftScene.updateGeometricState();
    }

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

When I run this test, the blue cube renders as a square in the center of the screen. When I click LMB the test switches to split-screen mode, with the red cube on the left half and the blue cube on the right half. Only both cubes now appear squashed horizontally, rendering as tall rectangles. I want them to render as squares.

I’ve tried various ways of configuring leftCam and rightCam, but I haven’t found one that works better than simply cloning cam and then setting a new viewport, as shown above. Any suggestions?

1 Like

Have you tried using

camera.resize(height,width/2f)

2 Likes

I’ve tried

rightCam.resize(width/2, height, true);

if that’s what you mean.

1 Like

Yeah that’s what I meant. This might not be helpful but I have it in my mind there was a bug that stopped you properly resizing viewports. I remember when I switched from 3.0 to 3.1 my game became distorted.

edit:

Might be the problem you are having?

2 Likes

Issue #357 does seem relevant. I’ll test with jME 3.0 and see if that helps.

1 Like

In jME 3.0, the code pasted above behaves just as in 3.1, so I don’t consider this a regression.

1 Like

I find I can use setFrustum() to configure the frustum I want for leftCam and rightCam in simpleInitApp().

Later, however, RenderManager.notifyReshape() invokes Camera.resize(w, h, true), changing the aspect ratio of my cameras back to that of the display.

It looks like the bug is in Camera.resize(). The following diff seems to fix it for my test code above (but not for Maud):

+++ b/jme3-core/src/main/java/com/jme3/renderer/Camera.java
@@ -456,7 +456,10 @@
         onViewPortChange();
 
         if (fixAspect /*&& !parallelProjection*/) {
-            frustumRight = frustumTop * ((float) width / height);
+            float h = height * (viewPortTop - viewPortBottom);
+            float w = width * (viewPortRight - viewPortLeft);
+            float aspectRatio = w / h;
+            frustumRight = frustumTop * aspectRatio;
             frustumLeft = -frustumRight;
             onFrustumChange();
         }

PostScript: By trial and error, I figured out how to make split-screen work for Maud also: invoke “resize(cam.getWidth(), cam.getHeight(), true)” on each camera after invoking setViewPort(). My test app doesn’t need this (though it doesn’t seem to hurt anything), and it’s not sufficient by itself.

Both Maud and the test app, however, need the patch to com.jme3.renderer.Camera in order to function properly.
I’ll update issue #357 and submit pull request on GitHub.

2 Likes