ThirdPersonHandler

Has anyone completed or started on a ThirdPersonHandler (or ThirdPersonCamera) for jme3? It would be much like the ThirdPersonHandler in jme2.

There were already people working on this, but I think no one has ever finished it.



I thought tim8dev and nehon were working on it, but I'm not sure. Maybe Norman knows who were it.

yeah i was working on it and i've been sidetracked whith shadows and AO…

anyway i've got a working but drafty chaseCam, if you want to use it :



import com.jme3.input.InputManager;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

public class ChaseCam{

    private Spatial target = null;
    private float distance = 20;
    private float minHeight = 0.05f;
    private float maxHeight = FastMath.PI / 2;
    private float minDistance = 1.0f;
    private float maxDistance = 40.0f;
    private float zoomSpeed = 2.0f;
    private float rotationSpeed = 1.0f;
    /**
     * the camera.
     */
    private Camera cam = null;
    private InputManager inputManager;
    private Vector3f initialUpVec;
    private boolean canRotate;
    private float rotation = FastMath.PI / 2;
    private float vRotation = 0;

    public ChaseCam(Camera cam, final Spatial target) {
        this.target = target;
        initialUpVec = cam.getUp().clone();

        this.cam = cam;
        updateCamera();

    }

  
    public void registerWithInput(InputManager inputManager) {
        String[] ActionInputs = {"toggleRotate"};
        String[] AnalogInputs = {"Down", "Up", "mouseLeft", "mouseRight", "ZoomIn", "ZoomOut"};

        this.inputManager = inputManager;

        inputManager.addMapping("Down", new MouseAxisTrigger(1, true));
        inputManager.addMapping("Up", new MouseAxisTrigger(1, false));

        inputManager.addMapping("ZoomIn", new MouseAxisTrigger(2, true));
        inputManager.addMapping("ZoomOut", new MouseAxisTrigger(2, false));
        inputManager.addMapping("mouseLeft", new MouseAxisTrigger(0, true));
        inputManager.addMapping("mouseRight", new MouseAxisTrigger(0, false));
        inputManager.addMapping("toggleRotate", new MouseButtonTrigger(0));
        inputManager.addMapping("toggleRotate", new MouseButtonTrigger(1));



        ActionListener al = new ActionListener() {

            public void onAction(String name, boolean keyPressed, float tpf) {

                if (name.equals("toggleRotate")) {
                    if (keyPressed) {
                        canRotate = true;
                    } else {
                        canRotate = false;
                    }

                }
            }
        };

        AnalogListener anl = new AnalogListener() {

            public void onAnalog(String name, float value, float tpf) {
                if (name.equals("mouseLeft")) {
                    rotateCamera(-value);
                } else if (name.equals("mouseRight")) {
                    rotateCamera(value);
                } else if (name.equals("Up")) {
                    vRotateCamera(value);
                } else if (name.equals("Down")) {
                    vRotateCamera(-value);
                } else if (name.equals("ZoomIn")) {
                    zoomCamera(value);
                } else if (name.equals("ZoomOut")) {
                    zoomCamera(-value);
                }
            }
        };
        inputManager.addListener(al, ActionInputs);
        inputManager.addListener(anl, AnalogInputs);
    }

    private void rotateCamera(float value) {

        if (!canRotate) {
            return;
        }
        rotation += value * rotationSpeed;
        updateCamera();
    }

    private void zoomCamera(float value) {
        ((Node)target).getChild(0).setCullHint(Spatial.CullHint.Dynamic);
        ((Node)target).getChild(0).setShadowMode(ShadowMode.CastAndRecieve);
        distance += value * zoomSpeed;
        if (distance > maxDistance) {
            distance = maxDistance;
        }
        if (distance < minDistance) {
            distance = minDistance;
        }
        if ((vRotation < minHeight) && (distance > (minDistance + 1.0f))) {
            vRotation = minHeight;

        }
        if (distance <= (minDistance + 1.0f)) {
            ((Node)target).getChild(0).setCullHint(Spatial.CullHint.Always);
            ((Node)target).getChild(0).setShadowMode(ShadowMode.Cast);
        }
        updateCamera();
    }

    private void vRotateCamera(float value) {
        if (!canRotate) {
            return;
        }
        vRotation += value * rotationSpeed;
        if (vRotation > maxHeight) {
            vRotation = maxHeight;
        }
        if ((vRotation < minHeight) && (distance > (minDistance + 1.0f))) {
            vRotation = minHeight;
        }
        updateCamera();
    }

    public void updateCamera() {

        float hDistance = distance * FastMath.sin((FastMath.PI / 2) - vRotation);
        Vector3f pos = new Vector3f(hDistance * FastMath.cos(rotation), distance * FastMath.sin(vRotation), hDistance * FastMath.sin(rotation));
        pos = pos.add(target.getLocalTranslation());
        cam.setLocation(pos);
        cam.lookAt(target.getLocalTranslation(), initialUpVec);

    }

    public void onPreUpdate(float tpf) {
        canRotate = false;

    }

    public void onPostUpdate(float tpf) {
        inputManager.setCursorVisible(!canRotate);
    }

    public float getMaxDistance() {
        return maxDistance;
    }

    public void setMaxDistance(float maxDistance) {
        this.maxDistance = maxDistance;
    }

    public float getMinDistance() {
        return minDistance;
    }

    public void setMinDistance(float minDistance) {
        this.minDistance = minDistance;
    }
  
}



be aware that it's assuming that the target is a node and that the first child is the model you want to chase.
to initialize it  you have to do that


        flyCam.setEnabled(false);
        ChaseCam chaser = new ChaseCam(viewPort.getCamera(),myTargetNode);      
        chaser.registerWithInput(inputManager);



and in the simpleUpdate method of your app (you'll need to override it)


@Override
    public void simpleUpdate(float tpf) {
        chaser.updateCamera();
    }



That's not great to have to call for an update but that is the simpliest way i found to do it...

The cam works like this :
- you can zoom in and out with the mouse wheel (default max zoomout distance is 40 but you can change it with setMaxDistance method)
- "grab" with left/right click moves the camera around the target
- if you completely zoom into the target, it culls it, set it to not receive shadow, and act like a first person camera

that's it... hope that helps

Nice, thanks Nehon. I'll take a look at this and see how well it works for me.