Problems with viewports management

Hi !



I think something is wrong in the way view-port is taken into account for projections.

I'm trying to define a view-port that is 16:9 whatever the resolution is, but when doing this, if the actual viewport does not entirely fits the window, all that is to be rendered in the ortho queue is mis-located (the error being larger as the object goes away from the origin).

There's also something wrong when using getScreenCoordinates() in such cases …





I've looked into JME's source and found that the setOrtho() method is using the width and height of the renderer instead of the width and height of camera's view-port :



    - current source :


GLU.gluOrtho2D(0, width, 0, height);



    - should be ? :


float viewportWidth = width * (camera.getViewPortRight() - camera.getViewPortLeft());
float viewportHeight = height * (camera.getViewPortTop() - camera.getViewPortBottom());
GLU.gluOrtho2D(0, viewportWidth, 0, viewportHeight);



Of course, the same with the setOrthoCenter() method.



However, I thought in first place that viewport's offset should be used as well instead of 0 in the call to GLU.gluOrtho2D(). That was not working, but adding a glTranslate() does the trick ... only for objects that are located using the getScreenCoordinates().
I finally managed to find a solution, but I don't really understand why it is working :
    - letting zeros in GLU.gluOrtho2D()
    - no glTranslate
    - the part I don't understand  :?, as current source seems to be ok -> changing the getScreenCoordinates() method :

        - current source :


store.x = ( ( tmp_quat.x + 1 ) * ( viewPortRight - viewPortLeft ) / 2 + viewPortLeft ) * getWidth();
store.y = ( ( tmp_quat.y + 1 ) * ( viewPortTop - viewPortBottom ) / 2 + viewPortBottom ) * getHeight();



        - is working :


store.x = ( ( tmp_quat.x + 1 ) * ( viewPortRight - viewPortLeft ) / 2 ) * getWidth();
store.y = ( ( tmp_quat.y + 1 ) * ( viewPortTop - viewPortBottom ) / 2 ) * getHeight();



Here again, the same with the getWorldCoordinates() method.



I sum-up all that -> what seems to be working is :


    public Vector3f getScreenCoordinates( Vector3f worldPosition, Vector3f store ) {
        if ( store == null ) {
            store = new Vector3f();
        }
        checkViewProjection();
        tmp_quat.set( worldPosition.x, worldPosition.y, worldPosition.z, 1 );
        modelViewProjection.mult( tmp_quat, tmp_quat );
        tmp_quat.multLocal( 1.0f / tmp_quat.w );
        store.x = ( ( tmp_quat.x + 1 ) * ( viewPortRight - viewPortLeft ) / 2  ) * getWidth();
        store.y = ( ( tmp_quat.y + 1 ) * ( viewPortTop - viewPortBottom ) / 2  ) * getHeight();
        store.z = ( tmp_quat.z + 1 ) / 2;

        return store;
    }

    public void setOrtho() {
        if (inOrthoMode) {
            throw new JmeException("Already in Orthographic mode.");
        }
        // set up ortho mode
        RendererRecord matRecord = (RendererRecord) DisplaySystem
                .getDisplaySystem().getCurrentContext().getRendererRecord();
        matRecord.switchMode(GL11.GL_PROJECTION);
        GL11.glPushMatrix();
        GL11.glLoadIdentity();
        float viewportWidth = width * (camera.getViewPortRight() - camera.getViewPortLeft());
        float viewportHeight = height * (camera.getViewPortTop() - camera.getViewPortBottom());
            GLU.gluOrtho2D(0, viewportWidth, 0, viewportHeight);
        matRecord.switchMode(GL11.GL_MODELVIEW);
        GL11.glPushMatrix();
        GL11.glLoadIdentity();
        inOrthoMode = true;
    }



Finally, width, height, getWidth(), getHeight() are used in lots of places, and I think it sounds reasonable to check their use against view-port's actual size.

Any suggestion on what is to be done ?

Interesting…  would it be possible to code a test showing the incorrect behavior?  I have never had issues with the viewport, so I am not sure how to easily validate your changes.

Hi

This is a short example showing my problem:



import com.jme.app.SimpleGame;
import com.jme.input.KeyInput;
import com.jme.input.KeyInputListener;
import com.jme.input.MouseInput;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Quad;
import com.jme.system.DisplaySystem;


public class TestClass extends SimpleGame implements KeyInputListener {
  private Box box2;
  private Quad quad;
  private Box box1;
  private boolean fullViewport;

  /**
  * Launches the test application.
  *
  * @param args application's arguments
  */
  public static void main(String[] args) {
    // Create Object
    TestClass app = new TestClass();

    // Signal to show properties dialog
    app.setConfigShowMode(ConfigShowMode.AlwaysShow);

    // Start the program
    app.start();
  }

  @Override
  protected void simpleInitGame() {
    MouseInput.get().setCursorVisible(true);
    KeyInput.get().addListener(this);

    DisplaySystem.getDisplaySystem().getRenderer().setBackgroundColor(ColorRGBA.blue);

    setFullViewport(false);

    // A box to check aspect ratio of 3D objects
    box1 = new Box("test box 1", new Vector3f(-1, -1, -1), new Vector3f(1, 1, 1));
    box1.setLocalTranslation(0, 5, 0);
    box1.setLocalRotation(new Quaternion(new float[] {(float) Math.toRadians(30),
      (float) Math.toRadians(45), (float) Math.toRadians(60)}));

    // Another one, not rotated
    box2 = new Box("test box 2", new Vector3f(-1, -1, -1), new Vector3f(1, 1, 1));
    box2.setLocalTranslation(0, -5, 0);

    // A text to check aspect ratio of 2D objects
    quad = new Quad("test quad", 100, 20);
    quad.setRenderQueueMode(Renderer.QUEUE_ORTHO);

    // Attach children
    rootNode.attachChild(box1);
    rootNode.attachChild(box2);
    rootNode.attachChild(quad);
}

  /**
  * Sets the viewport to take all display's available surface, or a portion of it.
  *
  * @param full set to true to take as much display's available surface as possible
  */
  private void setFullViewport(boolean full) {
    float vpWidth = full ? 1.f : 0.9f;
    float vpLeft = (1.f - vpWidth) / 2.f;
    float vpRight = vpLeft + vpWidth;

    float vpHeight = full ? 1.f : 0.75f;
    float vpBottom = (1.f - vpHeight) / 2.f;
    float vpTop = vpBottom + vpHeight;

    Camera camera = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
    camera.setViewPort(vpLeft, vpRight, vpBottom, vpTop);
    camera.setFrustumPerspective(60, vpWidth / vpHeight, 0.1f, 100);
    camera.update();
    camera.apply();

    fullViewport = full;
  }

  @Override
  protected void simpleUpdate() {
    // Center quad on box2 -> check that getScreenCoordinates is working fine (or not)
    Camera camera = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
    camera.getScreenCoordinates(box2.getWorldTranslation(), quad.getLocalTranslation());
  }

  /**
  * Toggle between viewport's modes when a 'V' key press is detected.
  *
  * @param character
  * @param keyCode
  * @param pressed
  * @see com.jme.input.KeyInputListener#onKey(char, int, boolean)
  */
  public void onKey(char character, int keyCode, boolean pressed) {
    if ((keyCode == KeyInput.KEY_V) && pressed) {
      setFullViewport(!fullViewport);
    }
  }
}

Applying the patches of the first post seems to work for objects in orthographic queue, but breaks statistics graph as it uses the getWidth() and getHeight() methods of the Renderer.

Any idea on what can be done ?

I've patched JME for now and for my needs, but is there a chance to get something better ? :smiley:

Yeah, MrCoder is looking into this as I understand it.

Cool !  :smiley:

Hello !



Is there any news concerning this issue ?

i have the same issues, i am creating a dualscreen window and set the viewport to setViewPort(0, 0.5f, 0, 1); so i can render to the left half of the window (the left screen). ortho stuff is messed up, since it seems to retrieve stuff from the renderer and calculates with the wrong width.