Camera view window


I’m writing a small soccer-style game for some artificial intelligence research. I’ve got a player who can be moved around and kick (by colliding with) a small ball into a goal. At present the scene is visualised using the

cam.lookAt(robotCharacter.getPhysicsLocation(), Vector3f.UNIT_Y);

command as in the tutorial. What I’d like to do now is have an additional camera which represents the scene from the point of view of the player (i.e what the player can ‘see’), and ideally, have the image from that camera appear in a small window (picture in picture style), and also be able to access this image for visual processing (edge detection etc.). Is this something thats doable? I imagine it would be best to have a CameraNode thats attached to the player spatial, but I’m not sure how to access its output…

Thanks very much


Take a look at TestMultiViews and TestRenderToMemory


that code is super relevant, thanks. I copied and pasted the code and ran it, and 3 of the cameras had highly distorted and pixelated images, and when I add another camera using the same techniques to my soccer world, the second camera shows a similarly pixelated image. is there any reason you can think of for this problem?

Hm, no idea

Perhaps you can post what code you have so far?

pre type="php"

package mygame;


import com.jme3.asset.TextureKey;

import com.jme3.bullet.control.RigidBodyControl;

import com.jme3.math.Vector2f;

import com.jme3.math.Vector3f;

import com.jme3.math.Matrix3f;

import com.jme3.renderer.queue.RenderQueue.ShadowMode;

import com.jme3.scene.Geometry;

import com.jme3.scene.shape.Box;

import com.jme3.scene.shape.Sphere;

import com.jme3.scene.shape.Sphere.TextureMode;

import com.jme3.shadow.BasicShadowRenderer;

import com.jme3.texture.Texture;

import com.jme3.texture.Texture.WrapMode;

import com.jme3.input.controls.AnalogListener;

import com.jme3.bullet.control.CharacterControl;

import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;

import com.jme3.scene.Spatial;

import com.jme3.bullet.BulletAppState;

import com.jme3.bullet.PhysicsSpace;

import com.jme3.input.KeyInput;

import com.jme3.input.controls.ActionListener;

import com.jme3.input.controls.KeyTrigger;

import com.jme3.material.Material;

import com.jme3.renderer.RenderManager;

import com.jme3.bullet.collision.shapes.SphereCollisionShape;

import com.jme3.light.AmbientLight;

import com.jme3.math.ColorRGBA;

import com.jme3.input.ChaseCamera;

import com.jme3.math.Quaternion;

import com.jme3.renderer.Camera;

import com.jme3.renderer.ViewPort;




public class soccerWorld extends SimpleApplication implements ActionListener{

  public static void main(String[] args) {

    soccerWorld app = new soccerWorld();



 Prepare the Physics Application State (jBullet) /

  private BulletAppState bulletAppState;

  /** Activate custom rendering of shadows 

  BasicShadowRenderer bsr;

  / Prepare Materials */

  Material wall_mat;

  Material stone_mat;

  Material floor_mat;

 Prepare geometries and physical nodes for bricks and cannon balls. /

  private RigidBodyControl    brick_phy;

  private RigidBodyControl    brick_phy2;

  private RigidBodyControl    brick_phy3;

  private RigidBodyControl    brick_phy4;

  private RigidBodyControl    ball_phy;

  private static final Sphere sphere;

  private RigidBodyControl    floor_phy;

  private static final Box    floor;

  private RigidBodyControl    floor_extension_phy;

  private RigidBodyControl    floor_extension_phy2;

  private RigidBodyControl    floor_extension_phy3;

  private static final Sphere bullet;

  private static final SphereCollisionShape bulletCollisionShape;

    /** Another camera so we can see what the player sees. 

  private ChaseCamera viewChaseCam;

  private Camera viewCam;

 / Prepare geometries and physical nodes for robots. */

  private RigidBodyControl       robot_phys;

  private Spatial                robot;

  private CharacterControl       robotCharacter;

  private Vector3f walkDirection = new Vector3f();

  private Vector3f viewDirection = new Vector3f( 1, 0, 0);

  static {

 Initialize the  ball geometry /

    sphere = new Sphere(32, 32, 0.2f, true, false);


    /** Initialize the floor geometry 

    floor = new Box(Vector3f.ZERO, 10f, 0.1f, 5f);

    floor.scaleTextureCoordinates(new Vector2f(3, 6));

    bullet = new Sphere(32, 32, 0.4f, true, false);


        bulletCollisionShape = new SphereCollisionShape(0.4f);



  public void simpleInitApp() {

    / Set up Physics Game */

    bulletAppState = new BulletAppState();


 Configure cam to look at scene /

    cam.setLocation(new Vector3f(0, 6f, 6f));

    cam.lookAt(Vector3f.ZERO, new Vector3f(0, 1, 0));


    cam.setViewPort(.5f, 1f, 0f, 1f);

    AmbientLight light = new AmbientLight();



            /** Add the robot box 

    Spatial robot = assetManager.loadModel(“Models/Sinbad/Sinbad.mesh.xml”);


    robotCharacter = new CharacterControl(new CapsuleCollisionShape(0.5f,1.8f), 1f);

    robotCharacter.setPhysicsLocation(new Vector3f(3, 6, 0));






         ///////Camera Stuff///////

        //disabling the flyCam (more than one position control on the same cam = mess)


    //creating the node

    viewCam = cam.clone();

       viewCam.setLocation(new Vector3f(0, 20f, 0f));

    viewCam.lookAt(Vector3f.ZERO, new Vector3f(0, 1, 0));



   // viewChaseCam = new ChaseCamera(viewCam,robot);

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

        //create viewing window for players vision

    ViewPort view2 = renderManager.createMainView(“Left”, viewCam);



    / Initialize the scene, materials, and physics space */









    public void simpleUpdate(float tpf) {



        cam.lookAt(robotCharacter.getPhysicsLocation(), Vector3f.UNIT_Y);

       //re setting the look at attribute so the cam looks at the geom


        viewCam.lookAt(robotCharacter.getPhysicsLocation(), Vector3f.UNIT_Y);


    public void onAction(String binding, boolean value, float tpf) {

      if (binding.equals(“stop”)) {



      if (binding.equals(“Lefts”)) {

            if (value) {

                walkDirection.addLocal(new Vector3f(-.1f, 0, 0));

            } else {

                walkDirection.addLocal(new Vector3f(.1f, 0, 0));


        } else if (binding.equals(“Rights”)) {

            if (value) {

                walkDirection.addLocal(new Vector3f(.1f, 0, 0));

            } else {

                walkDirection.addLocal(new Vector3f(-.1f, 0, 0));


        } else if (binding.equals(“Up”)) {

            if (value) {

                walkDirection.addLocal(new Vector3f(0, 0, -.1f));

            } else {

                walkDirection.addLocal(new Vector3f(0, 0, .1f));


        } else if (binding.equals(“Backward”)) {

            if (value) {

                walkDirection.addLocal(new Vector3f(viewDirection.mult((float)-0.1)));

            } else {

                walkDirection.addLocal(new Vector3f(viewDirection.mult((float)0.1)));


        } else if (binding.equals(“Space”)) {


        } else if(binding.equals(“Forward”))


            if (value) {

                walkDirection.addLocal(new Vector3f(viewDirection.mult((float)0.1)));

            } else {

                walkDirection.addLocal(new Vector3f(viewDirection.mult((float)-0.1)));




 Initialize the materials used in this scene. /

  public void initMaterials() {

    wall_mat = new Material(assetManager, “Common/MatDefs/Misc/SimpleTextured.j3md”);

    TextureKey key = new TextureKey(“Textures/Terrain/BrickWall/BrickWall.jpg”);


    Texture tex = assetManager.loadTexture(key);

    wall_mat.setTexture(“ColorMap”, tex);

    stone_mat = new Material(assetManager, “Common/MatDefs/Misc/SimpleTextured.j3md”);

    TextureKey key2 = new TextureKey(“Textures/Terrain/Rock/Rock.PNG”);


    Texture tex2 = assetManager.loadTexture(key2);

    stone_mat.setTexture(“ColorMap”, tex2);

    floor_mat = new Material(assetManager, “Common/MatDefs/Misc/SimpleTextured.j3md”);

    TextureKey key3 = new TextureKey(“Textures/Terrain/Pond/Pond.png”);


    Texture tex3 = assetManager.loadTexture(key3);


    floor_mat.setTexture(“ColorMap”, tex3);


  /** Make a solid floor and add it to the scene. 

  public void initFloor() {

    Geometry floor_geo = new Geometry(“Floor”, floor);



    floor_geo.setLocalTranslation(0, -0.1f, 0);


    / Make the floor physical with mass 0.0f! /

    floor_phy = new RigidBodyControl(0.0f);



      / Extend the floor, more room for soccer /

    Geometry floor_extension = new Geometry(“Floor”, floor);



    floor_extension.setLocalTranslation(10f, -0.1f, 0);


    floor_extension_phy = new RigidBodyControl(0.0f);



     Geometry floor_extension2 = new Geometry(“Floor”, floor);



    floor_extension2.setLocalTranslation(10f, -0.1f, 10f);


    floor_extension_phy2 = new RigidBodyControl(0.0f);



     Geometry floor_extension3 = new Geometry(“Floor”, floor);



    floor_extension3.setLocalTranslation(0, -0.1f, 10f);


    floor_extension_phy3 = new RigidBodyControl(0.0f);




  / Activate shadow casting and light direction */

  private void initShadows() {

    bsr = new BasicShadowRenderer(assetManager, 256);

    bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());


    // Default mode is Off – Every node declares own shadow mode!



  public void makeSoccerBall() {

 Create a soccer ball geometry and attach to scene graph. /

    Geometry ball_geo = new Geometry(“soccer ball”, sphere);



    /** Position the cannon ball and activate shadows 

    ball_geo.setLocalTranslation(new Vector3f(3, 6, 0));


    / Make the ball physcial with a mass > 0.0f */

    ball_phy = new RigidBodyControl(0.01f);

 Add physical ball to physics space. /




    private void setupKeys() {


        inputManager.addMapping(“Backward”, new KeyTrigger(KeyInput.KEY_J));

        inputManager.addMapping(“Forward”, new KeyTrigger(KeyInput.KEY_U));

        inputManager.addMapping(“Space”, new KeyTrigger(KeyInput.KEY_SPACE));

        inputManager.addMapping(“Rotate_right”, new KeyTrigger(KeyInput.KEY_K));

        inputManager.addMapping(“Rotate_left”, new KeyTrigger(KeyInput.KEY_H));

        inputManager.addMapping(“stop”, new KeyTrigger(KeyInput.KEY_B));

        inputManager.addListener(this, “Forward”);

        inputManager.addListener(this, “Backward”);

        inputManager.addListener(this, “Space”);

        inputManager.addListener(this, “stop”);

        inputManager.addListener(analogListener, new String[]{“Rotate_right”});

        inputManager.addListener(analogListener, new String[]{“Rotate_left”});





    public void simpleRender(RenderManager rm) {

        //TODO: add render code


  private PhysicsSpace getPhysicsSpace() {

        return bulletAppState.getPhysicsSpace();


   private AnalogListener analogListener = new AnalogListener() {

    public void onAnalog(String name, float value, float tpf) {

        if (name.equals(“Rotate_right”)) {

            Matrix3f mat = new Matrix3f();

            double theta = (double) 0.78f

            mat.setColumn(0, new Vector3f((float) Math.cos(theta),0,(float) Math.sin(theta)));

            mat.setColumn(1, new Vector3f(0,1,0));

            mat.setColumn(2, new Vector3f((float) -Math.sin(theta),0,(float) Math.cos(theta)));

            viewDirection = mat.mult(viewDirection);         


        if (name.equals(“Rotate_left”)) {

            Matrix3f mat = new Matrix3f();

            double theta = (double) -0.78fvalue;

            mat.setColumn(0, new Vector3f((float) Math.cos(theta),0,(float) Math.sin(theta)));

            mat.setColumn(1, new Vector3f(0,1,0));

            mat.setColumn(2, new Vector3f((float) -Math.sin(theta),0,(float) Math.cos(theta)));

            viewDirection = mat.mult(viewDirection);





public void makeWalls() {

 float brickLength = 15f;

 float brickWidth  = 0.5f;

 float brickHeight = 0.6f;

 Box box = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth);

 box.scaleTextureCoordinates(new Vector2f(10f, .5f));

 Geometry brick_geo = new Geometry(“brick”, box);



    /** Position the brick geometry and activate shadows 

    Vector3f loc = new Vector3f(5,1,-5);



    / Make brick physical with a mass > 0.0f. */

    brick_phy = new RigidBodyControl(0f);

 Add physical brick to physics space. /



     Box box2 = new Box(Vector3f.ZERO, brickLength, brickHeight, brickWidth);

    box.scaleTextureCoordinates(new Vector2f(10f, .5f));

    Geometry brick_geo2 = new Geometry(“brick”, box);



    /** Position the brick geometry and activate shadows 

    Vector3f loc2 = new Vector3f(5,1,15);



    / Make brick physical with a mass > 0.0f. */

    brick_phy2 = new RigidBodyControl(0f);

 Add physical brick to physics space. /



    Box box3 = new Box(Vector3f.ZERO, brickWidth, brickHeight,brickLength-5 );

    box3.scaleTextureCoordinates(new Vector2f(10f, .5f));

    Geometry brick_geo3 = new Geometry(“brick”, box3);



    /** Position the brick geometry and activate shadows 

    Vector3f loc3 = new Vector3f(20,1,5);



    / Make brick physical with a mass > 0.0f. */

    brick_phy3 = new RigidBodyControl(0f);

 Add physical brick to physics space. /



    Box box4 = new Box(Vector3f.ZERO, brickWidth, brickHeight,brickLength-5 );

    box.scaleTextureCoordinates(new Vector2f(10f, .5f));

    Geometry brick_geo4 = new Geometry(“brick”, box4);



    /** Position the brick geometry and activate shadows 

    Vector3f loc4 = new Vector3f(-10,1,5);



    / Make brick physical with a mass > 0.0f. */

    brick_phy4 = new RigidBodyControl(0f);

 Add physical brick to physics space. */






sorry that’s an enormous amount of annyoing code to post. but if you run it, you’ll see what I mean. The camera which is cloned from cam, i.e viewCam, does not render properly. I’m not really sure why…

but also, the testmultiviews file does the same thing for all the viewPorts except the one attached to the original (cam) camera, so maybe it is some sort of bug?