Stereo vision

Hi.

Is there a way to put the stereo vision into jME2? It should be similar to viewports in raw OpenGL. Stereo vision can be performed by eye separation - 2 identical scenes - one near another with slightly different perspective.


StereoProjection(float _left,float _right,float _bottom,float _top,float _near,float _far,float _zero_plane,float _dist,float _eye)
{
//    Perform the perspective projection for one eye's subfield.
//    The projection is in the direction of the negative z-axis.
        //            _left=-6.0;
        //            _right=6.0;
        //            _bottom=-4.8;
       //             _top=4.8;
//    [default: -6.0, 6.0, -4.8, 4.8]
//    left, right, bottom, top = the coordinate range, in the plane of zero parallax setting,
//         which will be displayed on the screen.
//         The ratio between (right-left) and (top-bottom) should equal the aspect
//    ratio of the display.


       //                  _near=6.0;
       //                  _far=-20.0;
//    [default: 6.0, -6.0]
//    near, far = the z-coordinate values of the clipping planes.

       //                  _zero_plane=0.0;
//    [default: 0.0]
//    zero_plane = the z-coordinate of the plane of zero parallax setting.

//    [default: 14.5]
      //                     _dist=10.5;
//   dist = the distance from the center of projection to the plane of zero parallax.

//    [default: -0.3]
      //                 _eye=-0.3;
//    eye = half the eye separation; positive for the right eye subfield,
//    negative for the left eye subfield.

 float   _dx = _right - _left;
 float   _dy = _top - _bottom;

 float   _xmid = (_right + _left) / 2.0;
 float   _ymid = (_top + _bottom) / 2.0;

 float   _clip_near = _dist + _zero_plane - _near;
 float   _clip_far  = _dist + _zero_plane - _far;

  float  _n_over_d = _clip_near / _dist;

 float   _topw = _n_over_d * _dy / 2.0;
 float   _bottomw = -_topw;
 float   _rightw = _n_over_d * (_dx / 2.0 - _eye);
 float   _leftw  = _n_over_d *(-_dx / 2.0 - _eye);

        // Create a fustrum, and shift it off axis
        // glTranslate() applies to PROJECTION matrix
  glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(_leftw,  _rightw,  _bottomw,  _topw, _clip_near,  _clip_far);
    glTranslatef(-_xmid - _eye,  -_ymid,  -_zero_plane -_dist);
}



And the invocation:

//left eye
glViewport(0, 0, ClientWidth/2, ClientHeight);
StereoProjection(-6, 6, -4.8, 4.8, 6, -20, 0 , 10.5, -0.3);
drawScene();

//right eye
glViewport(ClientWidth/2, 0, ClientWidth/2, ClientHeight);
StereoProjection(-6, 6, -4.8, 4.8, 6, -20, 0 , 10.5, 0.3);
drawScene();



How can I perform that?

http://www.jmonkeyengine.com/forum/index.php?topic=10145.0



EDIT:

After every drawing of your scene you have to call the renderers renderQueue() method to “flush” the queues.

thanks a lot. I am not familiar with JME. I have downloaded that code [developed by gibbon] and how can I invoke that?

Could you give me an example how to perform Stereo Side-By-Side with that code? :



public class Lesson8 extends BaseGame {
    private static final Logger logger = Logger.getLogger(Lesson8.class
            .getName());
   
    // the terrain we will drive over.
    private TerrainBlock tb;
    // fence that will keep us in.
    private ForceFieldFence fence;
    //Sky box (we update it each frame)
    private Skybox skybox;
    //the new player object
    private Vehicle player;
    //the flag to grab
    private Flag flag;
    //private ChaseCamera chaser;
    protected InputHandler input;
    //the timer
    protected Timer timer;
    // Our camera object for viewing the scene
    private Camera cam;
    //The chase camera, this will follow our player as he zooms around the level
    private ChaseCamera chaser;
    // the root node of the scene graph
    private Node scene;

    // display attributes for the window. We will keep these values
    // to allow the user to change them
    private int width, height, depth, freq;
    private boolean fullscreen;
   
    //store the normal of the terrain
    private Vector3f normal = new Vector3f();
   
    //height above ground level
    private float agl;
   
    /**
     * Main entry point of the application
     */
    public static void main(String[] args) {
        Lesson8 app = new Lesson8();
        // We will load our own "fantastic" Flag Rush logo. Yes, I'm an artist.
        app.setConfigShowMode(ConfigShowMode.AlwaysShow, Lesson8.class
                .getClassLoader().getResource(
                        "jmetest/data/images/FlagRush.png"));
        app.start();
    }

    /**
     * During an update we look for the escape button and update the timer
     * to get the framerate. Things are now starting to happen, so we will
     * update
     *
     * @see com.jme.app.BaseGame#update(float)
     */
    protected void update(float interpolation) {
        // update the time to get the framerate
        timer.update();
        interpolation = timer.getTimePerFrame();
        //update the keyboard input (move the player around)
        input.update(interpolation);
        //update the chase camera to handle the player moving around.
        chaser.update(interpolation);
        //update the fence to animate the force field texture
        fence.update(interpolation);
        //update the flag to make it flap in the wind
        flag.update(interpolation);
       
        //we want to keep the skybox around our eyes, so move it with
        //the camera
        skybox.setLocalTranslation(cam.getLocation());
       
        // if escape was pressed, we exit
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit")) {
            finished = true;
        }
       
        //We don't want the chase camera to go below the world, so always keep
        //it 2 units above the level.
        if(cam.getLocation().y < (tb.getHeight(cam.getLocation())+2)) {
            cam.getLocation().y = tb.getHeight(cam.getLocation()) + 2;
            cam.update();
        }
       
        //make sure that if the player left the level we don't crash. When we add collisions,
        //the fence will do its job and keep the player inside.
        float characterMinHeight = tb.getHeight(player
                .getLocalTranslation())+agl;
        if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) {
            player.getLocalTranslation().y = characterMinHeight;
        }
       
        //get the normal of the terrain at our current location. We then apply it to the up vector
        //of the player.
        tb.getSurfaceNormal(player.getLocalTranslation(), normal);
        if(normal != null) {
            player.rotateUpTo(normal);
        }
       
        //Because we are changing the scene (moving the skybox and player) we need to update
        //the graph.
        scene.updateGeometricState(interpolation, true);
    }

    /**
     * draws the scene graph
     *
     * @see com.jme.app.BaseGame#render(float)
     */
    protected void render(float interpolation) {
        // Clear the screen
        display.getRenderer().clearBuffers();
        display.getRenderer().draw(scene);
    }

    /**
     * initializes the display and camera.
     *
     * @see com.jme.app.BaseGame#initSystem()
     */
    protected void initSystem() {
        // store the settings information
        width = settings.getWidth();
        height = settings.getHeight();
        depth = settings.getDepth();
        freq = settings.getFrequency();
        fullscreen = settings.isFullscreen();
       
        try {
            display = DisplaySystem.getDisplaySystem(settings.getRenderer());
            display.createWindow(width, height, depth, freq, fullscreen);

            cam = display.getRenderer().createCamera(width, height);
        } catch (JmeException e) {
            logger.log(Level.SEVERE, "Could not create displaySystem", e);
            System.exit(1);
        }

        // set the background to black
        display.getRenderer().setBackgroundColor(ColorRGBA.black.clone());

        // initialize the camera
        cam.setFrustumPerspective(45.0f, (float) width / (float) height, 1,
                5000);
       
        cam.setLocation(new Vector3f(200,1000,200));
       
        /** Signal that we've changed our camera's location/frustum. */
        cam.update();

        /** Get a high resolution timer for FPS updates. */
        timer = Timer.getTimer();

        display.getRenderer().setCamera(cam);

        KeyBindingManager.getKeyBindingManager().set("exit",
                KeyInput.KEY_ESCAPE);
    }

    /**
     * initializes the scene
     *
     * @see com.jme.app.BaseGame#initGame()
     */
    protected void initGame() {
        display.setTitle("Flag Rush");
       
        scene = new Node("Scene graph node");
        /** Create a ZBuffer to display pixels closest to the camera above farther ones.  */
        ZBufferState buf = display.getRenderer().createZBufferState();
        buf.setEnabled(true);
        buf.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);
        scene.setRenderState(buf);
       
        //Time for a little optimization. We don't need to render back face triangles, so lets
        //not. This will give us a performance boost for very little effort.
      //  CullState cs = display.getRenderer().createCullState();
      //  cs.setCullFace(CullState.Face.Back);
      //  scene.setRenderState(cs);
       
        cam.setViewPort(0f, 0.5f, 0f, 1f);
        //Add terrain to the scene
        buildTerrain();
        //Add a flag randomly to the terrain
        buildFlag();
        //Light the world
        buildLighting();
        //add the force field fence
        buildEnvironment();
        //Add the skybox
        buildSkyBox();
        //Build the player
        buildPlayer();
        //build the chase cam
        buildChaseCamera();
        //build the player input
       
        cam.setViewPort(0.5f, 1.0f, 0f, 1f);
        //Add terrain to the scene
        buildTerrain();
        //Add a flag randomly to the terrain
        buildFlag();
        //Light the world
        buildLighting();
        //add the force field fence
        buildEnvironment();
        //Add the skybox
        buildSkyBox();
        //Build the player
        buildPlayer();
        //build the chase cam
        buildChaseCamera();
        //build the player input
       
        buildInput();
       
        // update the scene graph for rendering
        scene.updateGeometricState(0.0f, true);
        scene.updateRenderState();
    }

  
    private void buildFlag() {
        //create the flag and place it
        flag = new Flag(tb);
        scene.attachChild(flag);
        flag.placeFlag();
    }
   
 
    private void buildPlayer() {
  
        Node model = null;

        try {
           MaxToJme C1 = new MaxToJme();
           
           
           ByteArrayOutputStream BO = new ByteArrayOutputStream();
            URL maxFile = Lesson7.class.getClassLoader().getResource("jmetest/data/model/bodyall.3DS");
            C1.convert(new BufferedInputStream(maxFile.openStream()),BO);
            model = (Node)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            //scale it to be MUCH smaller than it is originally
           // model.setLocalScale(.0025f);
           model.setModelBound(new BoundingBox());
            model.updateModelBound();
           
            //scale it to be MUCH smaller than it is originally
            model.setLocalScale(.25f);
            model.setLocalTranslation(new Vector3f(0, -0.5f, 0));
            model.setLocalRotation(new Quaternion(new float[]{4.7f,4.7f,6.2f}));
     } catch (IOException e) {
            logger
                    .throwing(this.getClass().toString(), "buildPlayer()",
                            e);
        }
        player = new Vehicle("Player Node", model);
        player.setAcceleration(15);
        player.setBraking(15);
        player.setTurnSpeed(2.5f);
        player.setWeight(25);
        player.setMaxSpeed(25);
        player.setMinSpeed(15);
       
        player.setLocalTranslation(new Vector3f(100,0, 100));
        scene.attachChild(player);
        scene.updateGeometricState(0, true);
        //we now store this initial value, because we are rotating the wheels the bounding box will
        //change each frame.
        agl = ((BoundingBox)model.getWorldBound()).yExtent;
        player.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
    }
   
    /**
     * buildEnvironment will create a fence.
     */
    private void buildEnvironment() {
        //This is the main node of our fence
        fence = new ForceFieldFence("fence");
       
        //we will do a little 'tweaking' by hand to make it fit in the terrain a bit better.
        //first we'll scale the entire "model" by a factor of 5
        fence.setLocalScale(5);
        //now let's move the fence to to the height of the terrain and in a little bit.
        fence.setLocalTranslation(new Vector3f(25, tb.getHeight(25,25)+10, 25));
       
        scene.attachChild(fence);
    }

    /**
     * creates a light for the terrain.
     */
    private void buildLighting() {
        /** Set up a basic, default light. */
        DirectionalLight light = new DirectionalLight();
        light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
        light.setDirection(new Vector3f(1,-1,0));
        light.setEnabled(true);

          /** Attach the light to a lightState and the lightState to rootNode. */
        LightState lightState = display.getRenderer().createLightState();
        lightState.setEnabled(true);
        lightState.attach(light);
        scene.setRenderState(lightState);
    }

    /**
     * build the height map and terrain block.
     */
    private void buildTerrain() {
       
       
        MidPointHeightMap heightMap = new MidPointHeightMap(64, 1f);
        // Scale the data
        Vector3f terrainScale = new Vector3f(4, 0.0575f, 4);
        // create a terrainblock
         tb = new TerrainBlock("Terrain", heightMap.getSize(), terrainScale,
                heightMap.getHeightMap(), new Vector3f(0, 0, 0));

        tb.setModelBound(new BoundingBox());
        tb.updateModelBound();

        // generate a terrain texture with 2 textures
        ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
                heightMap);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/grassb.png")), -128, 0, 128);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/dirt.jpg")), 0, 128, 255);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/highest.jpg")), 128, 255,
                384);
        pt.createTexture(32);
       
        // assign the texture to the terrain
        TextureState ts = display.getRenderer().createTextureState();
        Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, true);
        ts.setTexture(t1, 0);
       
        //load a detail texture and set the combine modes for the two terrain textures.
        Texture t2 = TextureManager.loadTexture(
                TestTerrain.class.getClassLoader().getResource(
                "jmetest/data/texture/Detail.jpg"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear);

        ts.setTexture(t2, 1);
        t2.setWrap(Texture.WrapMode.Repeat);

        t1.setApply(Texture.ApplyMode.Combine);
        t1.setCombineFuncRGB(Texture.CombinerFunctionRGB.Modulate);
        t1.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
        t1.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
        t1.setCombineSrc1RGB(Texture.CombinerSource.PrimaryColor);
        t1.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);

        t2.setApply(Texture.ApplyMode.Combine);
        t2.setCombineFuncRGB(Texture.CombinerFunctionRGB.AddSigned);
        t2.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
        t2.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
        t2.setCombineSrc1RGB(Texture.CombinerSource.Previous);
        t2.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);

        tb.setRenderState(ts);
        //set the detail parameters.
        tb.setDetailTexture(1, 16);
        tb.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
        scene.attachChild(tb);
       
       
    }
   
    /**
     * buildSkyBox creates a new skybox object with all the proper textures. The
     * textures used are the standard skybox textures from all the tests.
     *
     */
    private void buildSkyBox() {
        skybox = new Skybox("skybox", 10, 10, 10);

        Texture north = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/north.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture south = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/south.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture east = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/east.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture west = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/west.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture up = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/top.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture down = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/bottom.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);

        skybox.setTexture(Skybox.Face.North, north);
        skybox.setTexture(Skybox.Face.West, west);
        skybox.setTexture(Skybox.Face.South, south);
        skybox.setTexture(Skybox.Face.East, east);
        skybox.setTexture(Skybox.Face.Up, up);
        skybox.setTexture(Skybox.Face.Down, down);
        skybox.preloadTextures();
        scene.attachChild(skybox);
    }
   
    /**
     * set the basic parameters of the chase camera. This includes the offset. We want
     * to be behind the vehicle and a little above it. So we will the offset as 0 for
     * x and z, but be 1.5 times higher than the node.
     *
     * We then set the roll out parameters (2 units is the closest the camera can get, and
     * 5 is the furthest).
     *
     */
    private void buildChaseCamera() {
        HashMap<String, Object> props = new HashMap<String, Object>();
        props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "6");
        props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "3");
        props.put(ThirdPersonMouseLook.PROP_MAXASCENT, ""+45 * FastMath.DEG_TO_RAD);
        props.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(5, 0, 30 * FastMath.DEG_TO_RAD));
        props.put(ChaseCamera.PROP_DAMPINGK, "4");
        props.put(ChaseCamera.PROP_SPRINGK, "9");
        chaser = new ChaseCamera(cam, player, props);
        chaser.setMaxDistance(8);
        chaser.setMinDistance(2);
    }

    /**
     * create our custom input handler.
     *
     */
    private void buildInput() {
        input = new FlagRushHandler(player, settings.getRenderer());
    }
   


    /**
     * will be called if the resolution changes
     *
     * @see com.jme.app.BaseGame#reinit()
     */
    protected void reinit() {
        display.recreateWindow(width, height, depth, freq, fullscreen);
    }
   
    /**
     * close the window and also exit the program.
     */
    protected void quit() {
        super.quit();
        System.exit(0);
    }

    /**
     * clean up the textures.
     *
     * @see com.jme.app.BaseGame#cleanup()
     */
    protected void cleanup() {

    }
}



I will really appreciate that!

Can anyone help? I have spent much time, but I did not figure out how to perform Stereo with above code.

lecter3 said:



public class Lesson8 extends BaseGame {
    // the second Camera:
    private Camera cam2;

    /**
     * draws the scene graph
     *
     * @see com.jme.app.BaseGame#render(float)
     */
    protected void render(float interpolation) {
        // clear the screen:
        display.getRenderer().clearBuffers();

        // set to the first camera:
        display.getRenderer().setCamera(cam);
        // Draw the first time (cam)
        display.getRenderer().draw(scene);

        // update location for cam2:
        Vector3f cam2loc = cam2.getLocation();
        cam2loc.set(cam.getLocation());
        cam2loc.addLocal(0.5f,0,0); // 0.5f means that the second camera is 0.5f units more right.
        cam2.setLocation(cam2Loc);
        // update rotation
        cam2.setAxes(cam.getLeft(), cam.getUp(), cam.getDirection());
        // update cam2:
        cam2.update();

        // set to the second camera:
        display.getRenderer().setCamera(cam2);
        // Draw the second time (cam2)
        display.getRenderer().draw(scene);
    }

    /**
     * initializes the display and camera.
     *
     * @see com.jme.app.BaseGame#initSystem()
     */
    protected void initSystem() {
        // store the settings information
        width = settings.getWidth();
        height = settings.getHeight();
        depth = settings.getDepth();
        freq = settings.getFrequency();
        fullscreen = settings.isFullscreen();
       
        try {
            display = DisplaySystem.getDisplaySystem(settings.getRenderer());
            display.createWindow(width, height, depth, freq, fullscreen);

            cam = display.getRenderer().createCamera(width, height);
            cam2 = display.getRenderer().createCamera(width, height);
        } catch (JmeException e) {
            logger.log(Level.SEVERE, "Could not create displaySystem", e);
            System.exit(1);
        }

        // set the background to black
        display.getRenderer().setBackgroundColor(ColorRGBA.black.clone());

        // initialize the camera
        cam.setFrustumPerspective(45.0f, (float) width / (float) height, 1,
                5000);
        cam2.setFrustumPerspective(45.0f, (float) width / (float) height, 1,
                5000);

        // set up the viewports: (cam is 0-0.5f/left-right; cam2 is 0.5f-1/left-right)
       cam.setViewport(0, 0.5f, 1, 1);
       cam2.setViewport(0.5f, 1, 1, 1);
       
       
        cam.setLocation(new Vector3f(200,1000,200));
        cam2.setLocation(new Vector3f(200,1000,200));
       
        /** Signal that we've changed our camera's location/frustum. */
        cam.update();
        cam2.update();

        /** Get a high resolution timer for FPS updates. */
        timer = Timer.getTimer();

        KeyBindingManager.getKeyBindingManager().set("exit",
                KeyInput.KEY_ESCAPE);
    }




Just paste the given methods and field into your class.. ;)
Hope it works..
If you want more or more detailed help, I'll help you of course :) but it would be helpful if you provide more information on what you exactly want tu do.

Thanks for your effort.



I have something like this:

http://www.youtube.com/watch?v=JwQfM2-8PJM



And I want to achieve that effect:

http://www.youtube.com/watch?v=zP7Q3WEUclM&feature=related



I have modified my code, taking into account your suggestions but I can see nothing - just a black screen.



Could you help me, please?



EDIT:

After this invocation everything goes black:

cam2.setViewPort(0.5f, 1, 1, 1);

Most of the methods in jME have parameter names that offer hints as to what they might have to be and there is much javadoc in the code aswell:



    /**
     * <code>setViewPort</code> sets the boundaries of the viewport
     *
     * @param left   the left boundary of the viewport
     * @param right  the right boundary of the viewport
     * @param bottom the bottom boundary of the viewport
     * @param top    the top boundary of the viewport
     */
    public void setViewPort( float left, float right, float bottom, float top ) {
        setViewPortLeft( left );
        setViewPortRight( right );
        setViewPortBottom( bottom );
        setViewPortTop( top );
    }



So what you want to call is

setViewPort(0.5f, 1, 0, 1);

dhdd said:

Most of the methods in jME have parameter names that offer hints as to what they might have to be and there is much javadoc in the code aswell:


    /**
     * <code>setViewPort</code> sets the boundaries of the viewport
     *
     * @param left   the left boundary of the viewport
     * @param right  the right boundary of the viewport
     * @param bottom the bottom boundary of the viewport
     * @param top    the top boundary of the viewport
     */
    public void setViewPort( float left, float right, float bottom, float top ) {
        setViewPortLeft( left );
        setViewPortRight( right );
        setViewPortBottom( bottom );
        setViewPortTop( top );
    }



So what you want to call is

setViewPort(0.5f, 1, 0, 1);




Oh.. I'm sorry! My fault, really. Both calls should have the zero as the 3rd param.