ThirdPersonHandler

Hi all,

Per has been so kind as to give me the code to his ThirdPerson spherical camera in his marble game. I polished it up a bit and made it into a fully fledged system with a test and stuff.



Anyways, heres the code:



KeySphericalCameraZoom


package com.jme.input.action;

/**
 *
 * Zoom in and out of the spherical camera system
 *
 * @author Ahmed
 * @version: $Id: KeySphericalCameraZoom.java, Dec 7, 2004 4:44:57 PM
 */
public class KeySphericalCameraZoom extends KeyInputAction {
   
   // a reference to the camera handler
   private SphericalCameraManager camHandler;
   
   // the new goal
   private float newGoal;
   
   /**
    * Constructor for the <code>KeySphericalCameraZoom</code>
    * KeyInputAction. It zooms out the camera by a set amount.
    * @param camH
    */
   public KeySphericalCameraZoom(SphericalCameraManager camH) {
      this.camHandler = camH;
   }
   
   /**
    * Set the new zoom on the camera in absolute values
    * @param goal
    */
   public void setNewZoomGoal(float goal) {
      newGoal = goal;
   }
   
   /**
    * Get the zoom on the camera in absolute values
    * @return
    */
   public float getNewZoomGoal() {
      return newGoal;
   }
   
   /**
    * Zoom in and out of the camera thing.
    */
   public void performAction(InputActionEvent evt) {
      camHandler.setGoalDistance(newGoal);
   }

}



KeySphericalCameraDown


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Mojo Monkey Coding, jME, jMonkey Engine, nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
package com.jme.input.action;

import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;
import com.jme.input.action.SphericalCameraManager;

/**
 * <code>KeySphericalCameraDown</code> changes the phi value
 * of the <code>SphericalCameraManager</code>
 *
 * @author Ahmed
 * @version: $Id: KeySphericalCameraDown.java, Dec 5, 2004 3:15:55 PM
 */
public class KeySphericalCameraDown extends KeyInputAction {
   
   // a reference to the cam manager
   private SphericalCameraManager camHandler;
   
   /**
    * Constructor takes a reference to the cam manager so
    * that it can change its values
    * @param manager
    */
   public KeySphericalCameraDown(SphericalCameraManager manager) {
      this.camHandler = manager;
      setAllowsRepeats(true);
      setSpeed(1);
   }
   
   /**
    * When the key is pressed, change the phi value
    */
   public void performAction(InputActionEvent event) {
      float phi = camHandler.getPhiGoal();
      
      phi += event.getTime() * getSpeed();
      
      camHandler.setPhiGoal(phi);
   }

}




KeySphericalCameraUp


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved. Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of the Mojo Monkey Coding, jME,
 * jMonkey Engine, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme.input.action;

import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;
import com.jme.input.action.SphericalCameraManager;

/**
 * <code>KeySphericalCameraUp</code> changes the phi value of the
 * <code>SphericalCameraManager</code>
 *
 * @author Ahmed
 * @version: $Id: KeySphericalCameraUp.java, Dec 5, 2004 3:14:29 PM
 */
public class KeySphericalCameraUp extends KeyInputAction {

   // a reference to the cam manager
   private SphericalCameraManager camHandler;

   /**
    * Constructor takes a reference to the cam manager so that it can change
    * its values
    *
    * @param manager
    */

   public KeySphericalCameraUp(SphericalCameraManager manager) {
      this.camHandler = manager;
      setAllowsRepeats(true);
      setSpeed(1);
   }
   
   /**
    * When the key is pressed, change the phi value
    */
   public void performAction(InputActionEvent event) {
      float phi = camHandler.getPhiGoal();

      phi += event.getTime() * getSpeed();

      camHandler.setPhiGoal(phi);
   }

}




KeySphericalCameraLeft

/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved. Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of the Mojo Monkey Coding, jME,
 * jMonkey Engine, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme.input.action;

import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;
import com.jme.input.action.SphericalCameraManager;

/**
 * <code>KeySphericalCameraLeft</code> changes the theta value of the
 * <code>SphericalCameraManager</code>
 *
 * @author Ahmed
 * @version: $Id: KeySphericalCameraLeft.java, Dec 5, 2004 3:10:16 PM
 */
public class KeySphericalCameraLeft extends KeyInputAction {

   // a reference to the cam manager
   private SphericalCameraManager camHandler;

   /**
    * Constructor takes a reference to the cam manager so that it can change
    * its values
    *
    * @param manager
    */
   public KeySphericalCameraLeft(SphericalCameraManager manager) {
      this.camHandler = manager;
      setAllowsRepeats(true);
      setSpeed(1);
   }
   
   /**
    * When the key is pressed, change the theta value
    */
   public void performAction(InputActionEvent event) {
      float theta = camHandler.getThetaGoal();

      theta -= event.getTime() * getSpeed();

      camHandler.setThetaGoal(theta);
   }

}



KeySphericalCameraRight


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved. Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of the Mojo Monkey Coding, jME,
 * jMonkey Engine, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme.input.action;

import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;
import com.jme.input.action.SphericalCameraManager;

/**
 * <code>KeySphericalCameraRight</code> changes the theta value of the
 * <code>SphericalCameraManager</code>
 *
 * @author Ahmed
 * @version: $Id: KeySphericalCameraRight.java, Dec 5, 2004 3:13:22 PM
 */
public class KeySphericalCameraRight extends KeyInputAction {

   // a reference to the cam manager
   private SphericalCameraManager camHandler;

   /**
    * Constructor takes a reference to the cam manager so that it can change
    * its values
    *
    * @param manager
    */
   public KeySphericalCameraRight(SphericalCameraManager manager) {
      this.camHandler = manager;
      setAllowsRepeats(true);
      setSpeed(1);
   }
   
   /**
    * When the key is pressed, change the theta value
    */
   public void performAction(InputActionEvent event) {
      float theta = camHandler.getThetaGoal();

      theta += event.getTime() * getSpeed();

      camHandler.setThetaGoal(theta);

   }

}



SphericalCameraManager


package com.jme.input.action;

import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.scene.Spatial;

/**
 * A class that converts spherical coordinates to caesterian coordinates.
 *
 * @author Ahmed
 * @version: $Id: SphericalCameraManager.java, Dec 4, 2004 7:45:59 PM
 */
public class SphericalCameraManager {

   // The maximum phi that it can be
   public static final float MAX_PHI = 179F * FastMath.DEG_TO_RAD;
   public static final float MIN_PHI = 1.0F * FastMath.DEG_TO_RAD;

   // some references to objects declared outside
   private Camera cam;
   private Spatial target;

   // theta, phi and the goalDistance are part of the spherical
   // coordinates.
   //
   // http://www.math.montana.edu/frankw/ccp/multiworld/multipleIVP/spherical/body.htm
   //
   // explains it pretty well
   private float theta;
   private float phi;
   private float goalDistance;

   // add some displacement to the target so its not
   // always centered
   private Vector3f displacement;

   // some vector3fs that will be used in the
   // update cycle. Created once so that no furthur
   // object instatiation takes place.
   private Vector3f cameraPos;
   private Vector3f cameraGoalPos;
   private Vector3f targetPos;
   private Vector3f dirVec;
   private Vector3f upVec;
   private Vector3f leftVec;

   // the same with floats, but not as severly
   private float distance;
   private float thetaDelta;
   private float phiDelta;
   private float x;
   private float y;
   private float z;

   /**
    * Constructor for the Spherical Camera manager. The first three are the
    * spherical coordinates and the last is the camera on which this class will
    * act on.
    *
    * @param theta
    * @param phi
    * @param distance
    * @param cam
    */
   public SphericalCameraManager(float theta,
         float phi,
         float distance,
         Camera cam) {
      this.theta = theta;
      this.phi = phi;
      this.distance = distance;
      this.theta = theta;
      this.phi = phi;
      goalDistance = distance;

      this.cam = cam;

      cameraPos = new Vector3f();
      cameraGoalPos = new Vector3f();
      targetPos = new Vector3f();
      dirVec = new Vector3f();
      upVec = new Vector3f();
   }

   /**
    * Set the target aiming spot
    *
    * @param target
    */
   public void setTarget(Spatial target) {
      this.target = target;
   }

   /**
    * Get the target aiming spot
    *
    * @return
    */
   public Spatial getTarget() {
      return target;
   }

   /**
    * Set the distance the camera should stay away from the object
    *
    * @param goal
    */
   public void setGoalDistance(float goal) {
      goalDistance = goal;
   }

   /**
    * Get the distance the camera should stay away from the object
    *
    * @return
    */
   public float getGoalDistance() {
      return goalDistance;
   }

   /**
    * Set the Theta (Horizontal angle) of the sphere coordinates
    *
    * @param th
    */
   public void setThetaGoal(float th) {
      theta = th;
   }

   /**
    * Get the Theta (Horizontal angle) of the sphere coordinates
    *
    * @return
    */
   public float getThetaGoal() {
      return theta;
   }

   /**
    * Set the Phi (the yaw) of the sphere coordinates
    *
    * @param phi
    */
   public void setPhiGoal(float phi) {
      if (this.phi > MAX_PHI) {
         this.phi = MAX_PHI;
      } else if (this.phi < MIN_PHI) {
         this.phi = MIN_PHI;
      }
   }

   /**
    * Get the Phi (yaw) of the sphere coordinates
    *
    * @return
    */
   public float getPhiGoal() {
      return phi;
   }

   /**
    * Set the displacement of the target
    *
    * @param dis
    */
   public void setDisplacement(Vector3f dis) {
      this.displacement = dis;
   }

   /**
    * Get the displacement of the target
    *
    * @return
    */
   public Vector3f getDisplacement() {
      return displacement;
   }

   /**
    * Convert the spherical into caesterian and set it to the camera. Also make
    * the camera look directly at the object
    *
    * @param interpolation
    */
   public void update(float interpolation) {

      // set the target position as the current
      // position of our reference spatial
      targetPos.x = target.getLocalTranslation().x;
      targetPos.y = target.getLocalTranslation().y;
      targetPos.z = target.getLocalTranslation().z;

      // also add some displacement if there is any
      targetPos.add(displacement);

      // set the position of the camera as to where it
      // actually is
      cameraPos.x = cam.getLocation().x;
      cameraPos.y = cam.getLocation().y;
      cameraPos.z = cam.getLocation().z;

      // add the detal distance between itself
      // and the goal distance
      distance += (goalDistance - distance);

      // convert spherical coordinates to caesterian
      // although do em wrong! but it still works...
      z = distance * FastMath.cos(theta) * FastMath.sin(phi);
      x = distance * FastMath.sin(theta) * FastMath.sin(phi);
      y = distance * FastMath.cos(phi);

      // add the camera's goal position to its position
      // so we get an absolute target
      cameraGoalPos.x = x + targetPos.x;
      cameraGoalPos.y = y + targetPos.y;
      cameraGoalPos.z = z + targetPos.z;

      // set the position of the camera to what it should be
      // either interpolate or just set it to the position
      // depending on the interpolation number
      if (interpolation != 0) {
         cameraPos.interpolate(cameraGoalPos, interpolation);
      } else {
         cameraPos.x = cameraGoalPos.x;
         cameraPos.y = cameraGoalPos.y;
         cameraPos.z = cameraGoalPos.z;
      }

      cam.setLocation(cameraPos);

      // look at the target
      lookAtTarget();
   }

   // look at the target by using a simple algorithm
   private void lookAtTarget() {
      dirVec.x = 0.0f;
      dirVec.y = 0.0f;
      dirVec.z = 0.0f;

      // find the distance between the target and the camera, then
      // normalise it
      dirVec.set(targetPos).subtractLocal(cameraPos).normalizeLocal();

      // obtain the UP vector of the camera
      upVec.x = cam.getUp().x;
      upVec.y = cam.getUp().y;
      upVec.z = cam.getUp().z;

      // find the left by doing a cross product between
      // the up and the direction. Normalise to obtain
      // a direction
      leftVec = upVec.cross(dirVec).normalizeLocal();

      // now find the proper up by doing the cross product
      // between the direction and the left and normalise
      // to obtain a direction
      upVec = dirVec.cross(leftVec).normalizeLocal();

      // set the axis of the camera as that.
      cam.setAxes(leftVec, upVec, dirVec);
      cam.onFrameChange();
   }
}




SphericalMouseLook


package com.jme.input.action;

import com.jme.input.Mouse;
import com.jme.input.MouseInput;
import com.jme.input.RelativeMouse;
import com.jme.input.action.InputActionEvent;
import com.jme.input.action.MouseInputAction;

/**
 * A MouseInputAction that performs tracks an object based on certain
 * parameters. Phi (how high in the sphere are we). Theta (horizontal) and the
 * distance (the radius of the circle). It will track
 *
 * @author Ahmed
 * @version: $Id: SphericalMouseLook.java, Dec 4, 2004 7:45:59 PM
 */
public class SphericalMouseLook implements MouseInputAction {

   // how to track the object. Ie. axis lock
   public static final int SML_TRACK_HORIZONTALLY = 0;
   public static final int SML_TRACK_VERTICALLY = 1;
   public static final int SML_TRACK_HORIZONTALLY_AND_VERTICALLY = 2;
   public static final int SML_TRACK_SPEED_ACCURATE = 4;
   public static final int SML_TRACK_SPEED_INTERPOLATE = 5;

   // which axis to lock
   private int axisLock;

   // how to position the camera
   private int positionMode;

   // is the mouse wheel responsible for zoom?
   private boolean wheelZoom;

   // some standard values about speed that can be changed
   private float speed = 1;
   private float trackingSpeed = 0;
   private float wheelSpeed = 0.2f;

   // some references to pre determined objects
   private RelativeMouse mouse;
   private SphericalCameraManager camHandler;
   private MouseInput input;

   // mojo?! a key in the mouse input?
   private String key;

   // action keys to associate a camera movement with the
   // press of a key. This is immitated by the movement of a
   // mouse
   private KeySphericalCameraLeft left;
   private KeySphericalCameraRight right;
   private KeySphericalCameraDown down;
   private KeySphericalCameraUp up;
   private KeySphericalCameraZoom zoom;

   // the input action event that will be triggered when
   // the mouse moves. Only the time will be set
   private InputActionEvent event;

   /**
    * A constructor that creates the MouseAction. It takes a predefined
    * SphericalCameraManager as an argument for processing.
    *
    * @param mouse
    * @param camHandler
    */
   public SphericalMouseLook(Mouse mouse, SphericalCameraManager camHandler) {
      this.mouse = (RelativeMouse) mouse;
      this.camHandler = camHandler;
      input = mouse.getMouseInput();
      axisLock = SphericalMouseLook.SML_TRACK_HORIZONTALLY;

      // initialise the keyInput actions
      left = new KeySphericalCameraLeft(camHandler);
      right = new KeySphericalCameraRight(camHandler);
      up = new KeySphericalCameraUp(camHandler);
      down = new KeySphericalCameraDown(camHandler);
      zoom = new KeySphericalCameraZoom(camHandler);

      // initialise the event
      event = new InputActionEvent();
   }

   /**
    * Perform the required action which is to incriment theta by the given
    * amount moved by the mouse. Then update the SphericalCameraManager to
    * convert theta and phi to caesterian coordinates
    */
   public void performAction(InputActionEvent evt) {

      int inputXDelta = input.getXDelta();
      int inputYDelta = input.getYDelta();
      float time = evt.getTime();
      event.setTime(time * getSpeed() * 5);

      // get theta and phi
      float thetaGoal = camHandler.getThetaGoal();
      float phiGoal = camHandler.getPhiGoal();

      // update theta and phi according to which
      // axis is meant to be locked to
      if (axisLock == SphericalMouseLook.SML_TRACK_HORIZONTALLY) {
         // check to see which way the mouse has moved
         // and perform the correct action
         if (inputXDelta > 0) {
            right.performAction(event);
         } else if (inputXDelta < 0) {
            left.performAction(event);
         }
      } else if (axisLock == SphericalMouseLook.SML_TRACK_VERTICALLY) {
         // check to see which way the mouse has moved
         // and perform the correct action
         if (inputYDelta == 1) {
            up.performAction(event);
         } else if (inputYDelta < 0) {
            down.performAction(event);
         }
         //phiGoal += (float) input.getYDelta() * time;
      } else if (axisLock == SphericalMouseLook.SML_TRACK_HORIZONTALLY_AND_VERTICALLY) {
         // check to see which way the mouse has moved
         // and perform the correct action
         if (inputXDelta == 1) {
            right.performAction(event);
         } else if (inputXDelta < 0) {
            left.performAction(event);
         }

         // check to see which way the mouse has moved
         // and perform the correct action
         if (inputYDelta == 1) {
            up.performAction(event);
         } else if (inputYDelta < 0) {
            down.performAction(event);
         }

         // check to see if the wheel zoom is enabled or
         // not
         if (wheelZoom == true) {
            // set the camera's zoom by settings its distance
            float zoomValue = camHandler.getGoalDistance() - (input.getWheelDelta() * wheelSpeed);
            zoom.setNewZoomGoal(zoomValue);
            zoom.performAction(event);
         }
      }
   }

   /**
    * Set the speed of movement
    */
   public void setSpeed(float speed) {
      this.speed = speed;
   }

   /**
    * Get the Speed of movement
    */
   public float getSpeed() {
      return speed;
   }

   /**
    * How fast should the camera react. A typical value is 10
    *
    * @param speed
    */
   public void setTrackingSpeed(float speed) {
      this.trackingSpeed = speed;
   }

   /**
    * How fast is the camera tracking the movement
    *
    * @return
    */
   public float getTrackingSPeed() {
      return trackingSpeed;
   }

   /**
    * Set the mouse input for this action
    */
   public void setMouse(Mouse mouse) {
      if (mouse instanceof RelativeMouse) {
         this.mouse = (RelativeMouse) mouse;
      } else {
         throw new ClassCastException(
               "The Mouse Supplied in SphericalMouseLock is not of RelativeMouse type");
      }
   }

   /**
    * Get the mouse of this inputAction;
    *
    * @return
    */
   public RelativeMouse getMouse() {
      return mouse;
   }

   /**
    * Which axis to lock to
    *
    * @param mode
    */
   public void setAxisLock(int mode) {
      axisLock = mode;
   }

   /**
    * Which axis (if any) have been locked to
    *
    * @return
    */
   public int getAxisLock() {
      return axisLock;
   }

   /**
    * Set the wheel lock. If true, the wheel zoom is enabled if false, wheel
    * zoom is disabled.
    *
    * @param locked
    */
   public void setWheelZoomEnabled(boolean locked) {
      wheelZoom = locked;
   }

   /**
    * Get the wheel zoom status
    *
    * @return
    */
   public boolean getWheelZoomEnabled() {
      return wheelZoom;
   }
   
   /**
    * Set the wheel speed for the zoom
    * @param speed
    */
   public void setWheelSpeed(float speed) {
      this.wheelSpeed = speed;
   }
   
   /**
    * Get the wheel speed for the zoom
    * @return
    */
   public float getWheelSpeed() {
      return wheelSpeed;
   }

   /**
    * Set to which way the camera behaves. Valid values are: <br>
    * SphericalMouseLock.SML_TRACK_SPEED_ACCURATE <br>
    * and <br>
    * SphericalMouseLock.SML_TRACK_SPEED_ACCURATE
    *
    * @param mode
    */
   public void setCameraPositionMode(int mode) {
      positionMode = mode;
   }

   /**
    * Get to which mode the camera positions itself
    *
    * @return
    */
   public float getCameraPositionMode() {
      return positionMode;
   }

   public SphericalCameraManager getSphericalCameraManager() {
      return camHandler;
   }

   public void setSphericalCameraManager(SphericalCameraManager man) {
      camHandler = man;
   }

   /**
    * Mojo? A key in a mouse action?
    */
   public void setKey(String key) {
      this.key = key;
   }

   /**
    * Dont get it!
    */
   public String getKey() {
      return key;
   }

}





ThirdPersonHandler


package com.jme.input;

import com.jme.app.AbstractGame;
import com.jme.input.InputHandler;
import com.jme.input.InputSystem;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.RelativeMouse;
import com.jme.input.action.KeyExitAction;
import com.jme.input.action.KeySphericalCameraDown;
import com.jme.input.action.KeySphericalCameraLeft;
import com.jme.input.action.KeySphericalCameraRight;
import com.jme.input.action.KeySphericalCameraUp;
import com.jme.input.action.SphericalCameraManager;
import com.jme.input.action.SphericalMouseLook;
import com.jme.math.FastMath;
import com.jme.renderer.Camera;
import com.jme.scene.Spatial;

/**
 * A Simple, extendable ThirdPersonHandler.
 *
 * @author Ahmed
 * @version: $Id: ThirdPersonHandler.java, Dec 4, 2004 8:44:00 PM
 */
public class ThirdPersonHandler extends InputHandler {

   // the keyboard manager
   private KeyBindingManager keyboard;

   // the relative mouse
   private RelativeMouse mouse;

   // the spherical mouse look input action
   private SphericalMouseLook sml;

   // the cameraManager
   private SphericalCameraManager camH;
   
   // the actions
   private KeySphericalCameraLeft left;
   private KeySphericalCameraRight right;
   private KeySphericalCameraUp up;
   private KeySphericalCameraDown down;
   
   

   /**
    * Constructor to initialise the ThirdPersonHandler
    *
    * @param app
    *            the application to which this input belongs to
    * @param cam
    *            the camera
    * @param spat
    *            the spatial to follow
    * @param distance
    *            the distance to keep away from the spatial
    * @param renderer
    *            the name of the renderer
    */
   public ThirdPersonHandler(AbstractGame app,
         Camera cam,
         Spatial spat,
         float distance,
         String renderer) {
      InputSystem.createInputSystem(renderer);

      setKeyBindings(app);
      setMouse();
      setActions(app, spat, distance, cam);
   }

   /**
    * Sets the "Exit" key input action
    *
    * @param app
    */
   private void setKeyBindings(AbstractGame app) {
      keyboard = KeyBindingManager.getKeyBindingManager();
      keyboard.setKeyInput(InputSystem.getKeyInput());

      keyboard.set("exit", KeyInput.KEY_ESCAPE);
      keyboard.set("left", KeyInput.KEY_A);
      keyboard.set("right", KeyInput.KEY_D);
      keyboard.set("up", KeyInput.KEY_W);
      keyboard.set("down", KeyInput.KEY_S);

      setKeyBindingManager(keyboard);
   }

   /**
    * Creates the mouse needed for the spherical mouse look
    */
   private void setMouse() {
      mouse = new RelativeMouse("Mouse Input");
      mouse.setMouseInput(InputSystem.getMouseInput());

      this.setMouse(mouse);
   }

   /**
    * Set the actions
    *
    * @param app
    * @param spat
    * @param distance
    */
   private void setActions(AbstractGame app, Spatial spat, float distance, Camera cam) {
      // exit
      KeyExitAction exit = new KeyExitAction(app);
      exit.setKey("exit");
      addAction(exit);

      // the camera manager
      camH = new SphericalCameraManager(0, 90 * FastMath.DEG_TO_RAD, distance, cam);
      camH.setTarget(spat);

      // left key
      left = new KeySphericalCameraLeft(camH);
      left.setKey("left");
      addAction(left);

      // right key
      right = new KeySphericalCameraRight(camH);
      right.setKey("right");
      addAction(right);

      // up Key
      up = new KeySphericalCameraUp(camH);
      up.setKey("up");
      addAction(up);

      // down key
      down = new KeySphericalCameraDown(camH);
      down.setKey("down");
      addAction(down);

      // mouse look
      sml = new SphericalMouseLook(mouse, camH);
      addAction(sml);
   }
   
   public void setGeneralSpeed(float speed) {
      left.setSpeed(speed);
      right.setSpeed(speed);
      up.setSpeed(speed);
      down.setSpeed(speed);
      sml.setSpeed(speed);
   }
   
   /**
    * super.update(time) and update the SphericalCameraManager too
    */
   public void update(float time) {
      super.update(time);
      // update the camera handler according to the
      // mode set
      if (sml.getCameraPositionMode() == SphericalMouseLook.SML_TRACK_SPEED_INTERPOLATE) {
         camH.update(time * sml.getTrackingSPeed());
      } else if (sml.getCameraPositionMode() == SphericalMouseLook.SML_TRACK_SPEED_ACCURATE) {
         camH.update(0);
      }
   }

   /**
    * Get the SphericalMouseLook of this inputHandler
    *
    * @return
    */
   public SphericalMouseLook getSphericalMouseLook() {
      return sml;
   }

}





Phewwwf, after all that, the test:
TestSphericalCamera


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved. Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of the Mojo Monkey Coding, jME,
 * jMonkey Engine, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package jmetest.input;

import java.net.URL;

import com.jme.app.SimpleGame;
import com.jme.image.Texture;
import com.jme.input.ThirdPersonHandler;
import com.jme.input.action.SphericalMouseLook;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.shape.Box;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.util.TextureManager;

/**
 * @author Ahmed
 * @version: $Id: TestSphericalCamera.java, Dec 4, 2004 7:45:59 PM
 */
public class TestSphericalCamera extends SimpleGame {

   protected void simpleInitGame() {
      rootNode = new Node("Root Node");

      Box box = new Box("Simple Box", new Vector3f(0, 0, 0), 1, 1, 1);
      box.getLocalTranslation().z = -25;
      rootNode.attachChild(box);

      URL imgURL = TestSphericalCamera.class.getClassLoader()
            .getResource("jmetest/data/images/Monkey.jpg");
      TextureState mnkyTS = display.getRenderer().createTextureState();
      mnkyTS.setEnabled(true);
      mnkyTS.setTexture(TextureManager.loadTexture(imgURL,
            Texture.FM_LINEAR,
            Texture.MM_LINEAR_LINEAR,
            true));
      box.setRenderState(mnkyTS);

      Box box2 = new Box("Simple Box 2", new Vector3f(0, -5, 0), 50, 2, 50);
      box2.setSolidColor(new ColorRGBA(0, 1, 1, 0.5f));
      rootNode.attachChild(box2);

      ZBufferState zEnabled = display.getRenderer().createZBufferState();
      zEnabled.setEnabled(true);
      rootNode.setRenderState(zEnabled);

      input = new ThirdPersonHandler(this, display.getRenderer().getCamera(),
            box, 15, properties.getRenderer());

      ThirdPersonHandler tph = (ThirdPersonHandler) input;
      
      tph.getSphericalMouseLook()
            .setAxisLock(SphericalMouseLook.SML_TRACK_HORIZONTALLY);
      tph
            .getSphericalMouseLook()
            .setCameraPositionMode(SphericalMouseLook.SML_TRACK_SPEED_ACCURATE);

      rootNode.updateGeometricState(0.0f, true);
      rootNode.updateRenderState();
   }

   protected void reinit() {
   }

   protected void cleanup() {
   }

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




Tired after all that copy/pasting! DP

This could really help me. :smiley: Could you change it so that you do not have to use the renderer’s camera?

Doh! I too made a cleanup of the camera sys which I was going to send to you :slight_smile:



I think you forgot to attach the test? You just posted the ThirdPersonHandler one more time.


Could you change it so that you do not have to use the renderer's camera?


Done...

But can I ask why you would need to do that?

DP

Phewf, i changed a few bits around with it to make it keyboard friendly too!



So in the test…WASD also moves the camera.



DP

Looks good as a straightforward basic 3rd person handler. I’m also not sure why you wouldn’t generally want to use the renderer’s camera, but perhaps the odd exception would come up.

// mojo?! a key in the mouse input?


key as in key/value pair. Not keyboard.

Man, that’s about the ugliest looking test I’ve ever seen.



Everything else looks good, I’ll remove those comments, and do something with the test and get it in.



EDIT:



Actually a comment… I can rotate around the object… and rotate around the object. I can’t move the object… this shows spherical rotation but not ThirdPersonController. I should be able to move the box around showing how the camera compensates.



EDIT 2:


Phewf, i changed a few bits around with it to make it keyboard friendly too!

So in the test...WASD also moves the camera.

DP


Ok, you made this change in ThirdPersonController itself? How do you plan on moving your spatial with this controller then?

Before committing this, DP should really put back that mouse wheel handling he removed from SphericalMouseLook.



He removed it because it didn’t work properly on his side, but we came to the conclusion that the problem was his very old mouse.

Oh, and DP, did you remove that way of putting in displacements to the targetpos? That’s a really important part of a third person camera handler, since you in almost every case wants at least a displacement in the Y axis, so that the camera is positioned above the character, like in Marble Fun.



Also displacements in X and Z is great for making shake effects and the like…

Per, maybe you should simply post the classes instead and we can work off your version…

Sure, ok, but I’ve got no input handler or test ready for use tho…



But here it comes:



CameraHandler (SphericalCameraManager):

/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved. Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of the Mojo Monkey Coding, jME,
 * jMonkey Engine, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package camerasys;

import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.system.DisplaySystem;
import com.jme.scene.Spatial;

/**
 * This class handles the camera bahavior - it's locked to a target and is
 * positioned using spherical coordinates. It always faces its target.
 *
 * setGoalAngles(float, float) and setGoalDistance(float) is the
 * <code>SphericalMouseLook</code>s interface. The camera strives to reach these
 * angles.
 *
 * It should also be said that the camera can't flip in the vertical axis.
 *
 * @author Per Thulin
 */
public class CameraHandler {
   private static final float MAX_PHI = 179*FastMath.DEG_TO_RAD;
   private static final float MIN_PHI = 1*FastMath.DEG_TO_RAD;
   
   // These are the angles the camera strives to have. They are set by the
   // InputHandler.
   private float theta, phi;
   
   // The camera that is to be handled.
   private Camera cam;
   
   // This is what the camera should look at.
   private Spatial lookAtTarget;
   
   // This is what the camera should follow.
   private Spatial followTarget;
   
   // The distance between the camera and the target.
   private float distance;
   
   // The camera strives to have this distance to the target.
   private float goalDistance;
   
   // The speed in which the camera zooms.
   private float zoomSpeed;
   
   // The cameras reaction value.
   private float movementSpeed;
   
   // Definable camera displacements.
   private Vector3f displacement;
   
   // How close and far the camera can be to the target.
   private float minDistance, maxDistance;
   
   // Temp variables declared here to flatline memory usage.
   private Vector3f cameraGoalPos;
   private Vector3f followTargetPos;
   private Vector3f lookAtTargetPos;
   private Vector3f sphere;
   private Vector3f dirVec, upVec, leftVec;
   private float thetaDelta, phiDelta;
   private float deltaDistance;
   private float speed;
   
   /**
    * Creates a new CameraHandler. Note that you would have to set a target
    * after calling this.
    * @param theta The theta angle between the target and the camera.
    * @param phi The phi angle between the target and the camera.
    * @param distance The distance between the camera and the target.
    */
   public CameraHandler(float theta, float phi, float distance) {
      this.theta = theta;
      this.phi = phi;
      this.distance = distance;
      
      this.theta = theta;
      this.phi = phi;
      
      goalDistance = distance;
      
      cam = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
      
      //cameraPos = new Vector3f();
      cameraGoalPos = new Vector3f();
      followTargetPos = new Vector3f();
      lookAtTargetPos = new Vector3f();
      sphere = new Vector3f();
      dirVec = new Vector3f();
      upVec = new Vector3f();
      
      displacement = new Vector3f();
      
      // Some default values.
      zoomSpeed = 1.5f;
      movementSpeed = 100;
      
      minDistance = 25;
      maxDistance = 200;
   }
   
   /**
    * Sets the target this camera should follow and look at.
    * @param target
    */
   public void setTarget(Spatial target) {
      this.followTarget = target;
      this.lookAtTarget = target;
   }
   
   /**
    * Sets the target this camera should follow.
    * @param target
    */
   public void setFollowTarget(Spatial target) {
      this.followTarget = target;
   }
   
   /**
    * Sets the target this camera should look at.
    * @param target
    */
   public void setLookAtTarget(Spatial target) {
      this.lookAtTarget = target;
   }
   
   /**
    * Sets the goal distance between the camera and the target.
    * @param goal The goal radius of the coordinates sphere.
    */
   public void setGoalDistance(float goal) {
      if (goal < minDistance) this.goalDistance = minDistance;
      else if (goal > maxDistance) this.goalDistance = maxDistance;
      else this.goalDistance = goal;
   }
   
   /**
    * Gets the goal distance between the camera and the target.
    * @return The goal radius of the coordinates sphere.
    */
   public float getGoalDistance() {
      return goalDistance;
   }
   
   /**
    * Sets the goal theta angle the camera strives to have.
    * @param theta The angle in the horizontal axis.
    */
   public void setThetaGoal(float theta) {
      this.theta = theta;
   }
   
   /**
    * Sets the goal phi angle the camera strives to have.
    * @param theta The angle in the vertical axis.
    */
   public void setPhiGoal(float phi) {
      this.phi = phi;
      if (this.phi > MAX_PHI) this.phi = MAX_PHI;
      else if (this.phi < MIN_PHI) this.phi = MIN_PHI;
   }
   
   /**
    * Gets the goal theta angle.
    * @return The angle in the horizontal axis.
    */
   public float getThetaGoal() {
      return theta;
   }
   
   /**
    * Gets the goal phi angle.
    * @return The angle in the vertical axis.
    */
   public float getPhiGoal() {
      return phi;
   }
   
   /**
    * Sets the new camera position and direction based on spherical coordinates
    * around the target.
    */
   public void update(float tpf) {   
      speed = tpf * movementSpeed;
      if (speed > 1) speed = 1;
      
      followTargetPos.x = followTarget.getLocalTranslation().x + displacement.x;
      followTargetPos.y = followTarget.getLocalTranslation().y + displacement.y;
      followTargetPos.z = followTarget.getLocalTranslation().z + displacement.z;
      
      // Compute the radius.
      deltaDistance = goalDistance - distance;
      deltaDistance *= (tpf*zoomSpeed);
      distance += deltaDistance;
      
      // Use spherical coordinates to set the new camera position. distance
      // is the radius of the sphere.
      sphere.z = distance * FastMath.cos(theta) * FastMath.sin(phi);
      sphere.x = distance * FastMath.sin(theta) * FastMath.sin(phi);
      sphere.y = distance * FastMath.cos(phi);
      
      cameraGoalPos.x = sphere.x + followTargetPos.x;
      cameraGoalPos.y = sphere.y + followTargetPos.y;
      cameraGoalPos.z = sphere.z + followTargetPos.z;
      
      cam.getLocation().interpolate(cameraGoalPos, speed);
      
      //Make camera look at the target.
      lookAtTarget();
   }
   
   /**
    * Makes the camera face its target.
    */
   private void lookAtTarget() {
      lookAtTargetPos.x = lookAtTarget.getLocalTranslation().x + displacement.x;
      lookAtTargetPos.y = lookAtTarget.getLocalTranslation().y + displacement.y;
      lookAtTargetPos.z = lookAtTarget.getLocalTranslation().z + displacement.z;
      
      dirVec.x = 0;
      dirVec.y = 0;
      dirVec.z = 0;
      
      dirVec.set(lookAtTargetPos).subtractLocal(cam.getLocation()).normalizeLocal();
      
      upVec.x = 0;
      upVec.y = 1;
      upVec.z = 0;
      
      leftVec = upVec.cross(dirVec).normalizeLocal();
      upVec = dirVec.cross(leftVec).normalizeLocal();
      
      cam.setAxes(leftVec, upVec, dirVec);
   }
   
   public void setZoomSpeed(float speed) {
      zoomSpeed = speed;
   }
   
   public void setMovementSpeed(float speed) {
      movementSpeed = speed;
   }
   
   public void setDisplacement(Vector3f displacement) {
      this.displacement = displacement;
   }
   
   public Vector3f getDisplacement() {
      return displacement;
   }
   
   public void setMaxDistance(float distance) {
      this.maxDistance = distance;
   }
   
   public void setMinDistance(float distance) {
      this.minDistance = distance;
   }
   
   public float getMaxDistance() {
      return maxDistance;
   }
   
   public float getMinDistance() {
      return minDistance;
   }
   
   public float getZoomSpeed() {
      return zoomSpeed;
   }
   
   public Spatial getFollowTarget() {
      return followTarget;
   }
   
   public Spatial getLookAtTarget() {
      return lookAtTarget;
   }
}



SphericalMouseLook:

/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved. Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer. Redistributions in binary
 * form must reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other materials provided
 * with the distribution. Neither the name of the Mojo Monkey Coding, jME,
 * jMonkey Engine, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package camerasys;

import com.jme.input.*;
import com.jme.input.action.*;

/**
 * This class retrieves input from the passed mouse, and uses it to alter the
 * goal angles located in the passed <code>CameraHandler</code>.
 *
 * @author Per Thulin
 */
public class SphericalMouseLook implements MouseInputAction {   
   
   // The speed of the mouse wheel.
   private float wheelSpeed;
   
   // The mouse we'll retrieve input from.
   private RelativeMouse mouse;
   
   // The camera where the angles are stored.
   private CameraHandler camHandler;
   
   private boolean lockHorizontal, lockVertical, lockWheel;
   
   private boolean inverseVertical;
   
   private float moveSpeed;
   
   private String key;
   
   // Temp variables used in performAction.
   private float time;
   private float thetaGoal, phiGoal;
   private MouseInput input;
   
   /**
    * @param mouse The mouse to retrieve input from.
    * @param camHandler The cam where the angles are stored.
    */
   public SphericalMouseLook(Mouse mouse, CameraHandler camHandler) {
      this.mouse = (RelativeMouse)mouse;
      this.camHandler = camHandler;
      
      // Default values.
      moveSpeed = 0.3f;
      wheelSpeed = 0.2f;
      
      input = mouse.getMouseInput();
   }
   
   public void performAction(InputActionEvent evt) {
      time = evt.getTime() * moveSpeed;
      
      thetaGoal = camHandler.getThetaGoal();
      phiGoal = camHandler.getPhiGoal();
      
      if (!lockHorizontal) {
         if (inverseVertical) thetaGoal += (input.getXDelta() * time);
         else thetaGoal -= (input.getXDelta() * time);
         camHandler.setThetaGoal(thetaGoal);
      }
      if (!lockVertical) {
         phiGoal   += (input.getYDelta() * time);
         camHandler.setPhiGoal(phiGoal);
      }
      
      // Handle mouse wheel.
      if (!lockWheel) {
         camHandler.setGoalDistance(camHandler.getGoalDistance()
               - input.getWheelDelta() * wheelSpeed);
      }
   }
   
   public void setSpeed(float speed) {
      moveSpeed = speed;
   }
   
   public void setWheelSpeed(float speed) {
      wheelSpeed = speed;
   }
   
   public float getSpeed() {
      return moveSpeed;
   }
   
   public float getWheelSpeed() {
      return wheelSpeed;
   }
   
   public void setMouse(Mouse mouse) {
      this.mouse = (RelativeMouse)mouse;
   }
   
   public void setKey(String key) {
      this.key = key;
   }
   
   public String getKey() {
      return key;
   }
   
   public void setLockHorizontal(boolean lock) {
      this.lockHorizontal = lock;
   }
   
   public void setLockVertical(boolean lock) {
      this.lockVertical = lock;
   }
   
   public void setLockWheel(boolean lock) {
      this.lockWheel = lock;
   }
   
   public void setInverseVertical(boolean inverse) {
      this.inverseVertical = inverse;
   }
   
   public boolean getLockHorizontal() {
      return lockHorizontal;
   }
   
   public boolean getLockVertical() {
      return lockVertical;
   }
   
   public boolean getLockWheel() {
      return lockWheel;
   }
   
   public boolean getInverseVertical() {
      return inverseVertical;
   }
}



EDIT: Mixed up horizontal and vertical. Fixed now.

I edited the post above to fix some minor things.

my system works exactly like per’s and ive added improvements and bug fixes…



e.g:



upVec.x = 0;
upVec.y = 1;
upVec.z = 0;



I made it so that it uses the proper up axis of the camera. Thats a simple fix. Also, the cameraPos.interpolate(cameraGoalPos, interpolation) is not what you always want, sometimes you just want to set it straight, so i did that too.

I also added classes that can control key input too.

Per, il add your displacement, dont worry :)

DP

ive added your displacement thingymabob per. Its in SphericalCameraManager.



You set it by:



thirdPersonHandler.getSphericalMouseLook().getSphericalCameraManager().setDisplacement(new Vector3f(0, 11, 0));



DP

Cool :slight_smile:

Ok per, ive added wheel zoom.



You set it by:



tph.getSphericalMouseLook().setWheelZoomEnabled(true);
tph.getSphericalMouseLook().setWheelSpeed(0.2f);



I think this is now finished...NEXT!

DP

little copy/paste bug :



phi += event.getTime() * getSpeed();



in camera up and in camera down too



(there should be a - somewhere, or a "invertmouse * phi += event.getTime() * getSpeed(); "


big bug : vertical mouse look doesn't work !

please test if you can look up and down with your code, i may have made a mistake somewhere on my side !
suggestion :

maybe you can do a little refactoring where no set of axislock = no lock (more intuitive !)
"simon56" wrote:
please test if you can look up and down with your code, i may have made a mistake somewhere on my side !
I don't think you've done anything wrong - I'm experiencing the same.

Also camera displacements wont work in the state it is now. It's an easy fix and I've contacted DP about this.

doh!



When i get back home, il fix them all. ://