Chase cam issues (sort of)

took a copy of the chase cam as it stands now in svn and merged it with the functionality of the Physical chase cam. (the original writer of the phys cam either removed or didn’t have some functions that the original chasecam have now, so I couldn’t use it as is) …anyways after crudely slapping this all together it worked fine except that the set Minimum rotation was being ignored,



I compared the physics cam with the normal one and they were essentially the same…save some redesign and the physics ray checks, then I test another copy of the walkingChar test with a min vertical rot of 75 but f you zoomed to the minimum distance, minimum rotation would be ignored and the camera makes the full 90 degree rotation on -y and clips into the character…my merged version had the opposite effect at the maximum distance it would clip into the character ignoring the min vertical rot, but would respect the 75deg angle at the minimum distance :?



…anyhow I was able to solve my clipping issues by commenting out [java]&& (targetDistance > (minDistance + 1.0f))[/java] in methods vRotateCamera(float value) & zoomCamera(float value) and playing with the distance and min Rotation values. I think this is a bug in the jme3 version though



just for kicks the merged chase cam



[java]/*

  • Copyright © 2009-2010 jMonkeyEngine
  • 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 ‘jMonkeyEngine’ 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 hks.input;



    import com.jme3.bullet.PhysicsSpace;

    import com.jme3.bullet.collision.PhysicsRayTestResult;

    import com.jme3.export.InputCapsule;

    import com.jme3.export.JmeExporter;

    import com.jme3.export.JmeImporter;

    import com.jme3.export.OutputCapsule;

    import com.jme3.input.InputManager;

    import com.jme3.input.MouseInput;

    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.input.controls.Trigger;

    import com.jme3.math.FastMath;

    import com.jme3.math.Vector3f;

    import com.jme3.renderer.Camera;

    import com.jme3.renderer.RenderManager;

    import com.jme3.renderer.ViewPort;

    import com.jme3.scene.Spatial;

    import com.jme3.scene.control.Control;

    import java.io.IOException;

    import java.util.LinkedList;



    /**
  • A camera that follows a spatial and can turn around it by dragging the mouse
  • @author nehon

    */

    public class ChaseCamera2 implements ActionListener, AnalogListener, Control {



    private Spatial target = null;

    private float minVerticalRotation = 0.00f;

    private float maxVerticalRotation = FastMath.PI / 2;

    private float minDistance = 1.0f;

    private float maxDistance = 40.0f;

    private float maxDistanceByZoom = 20.0f;

    private float distance = 20;

    private float zoomSpeed = 2f;

    private float rotationSpeed = 1.0f;

    private float rotation = 0;

    private float trailingRotationInertia = 0.05f;

    private float zoomSensitivity = 5f;

    private float rotationSensitivity = 5f;

    private float chasingSensitivity = 5f;

    private float trailingSensitivity = 0.5f;

    private float vRotation = FastMath.PI / 6;

    private boolean smoothMotion = false;

    private boolean trailingEnabled = true;

    private float rotationLerpFactor = 0;

    private float trailingLerpFactor = 0;

    private boolean rotating = false;

    private boolean vRotating = false;

    private float targetRotation = rotation;

    private InputManager inputManager;

    private Vector3f initialUpVec;

    private float targetVRotation = vRotation;

    private float vRotationLerpFactor = 0;

    private float targetDistance = distance;

    private float distanceLerpFactor = 0;

    private boolean zooming = false;

    private boolean trailing = false;

    private boolean chasing = false;

    private boolean canRotate;

    private float offsetDistance = 0.002f;

    private Vector3f prevPos;

    private boolean targetMoves = false;

    private boolean enabled = true;

    private Camera cam = null;

    private final Vector3f targetDir = new Vector3f();

    private float previousTargetRotation;

    private final Vector3f pos = new Vector3f();

    protected Vector3f targetLocation = new Vector3f(0, 0, 0);

    protected boolean dragToRotate = true;

    protected Vector3f lookAtOffset = new Vector3f(0, 0, 0);

    protected boolean leftClickRotate = true;

    protected boolean rightClickRotate = true;

    private Vector3f temp = new Vector3f(0, 0, 0);

    protected boolean invertYaxis = false;

    protected boolean invertXaxis = false;

    private final static String ChaseCamDown = "ChaseCamDown";

    private final static String ChaseCamUp = "ChaseCamUp";

    private final static String ChaseCamZoomIn = "ChaseCamZoomIn";

    private final static String ChaseCamZoomOut = "ChaseCamZoomOut";

    private final static String ChaseCamMoveLeft = "ChaseCamMoveLeft";

    private final static String ChaseCamMoveRight = "ChaseCamMoveRight";

    private final static String ChaseCamToggleRotate = "ChaseCamToggleRotate";

    private Vector3f maxPos = new Vector3f();

    private PhysicsSpace myPhysicsSpace;



    /**
  • Constructs the chase camera
  • @param cam the application camera
  • @param target the spatial to follow

    */

    public ChaseCamera2(Camera cam, final Spatial target) {

    this(cam);

    target.addControl(this);

    }



    /**
  • Constructs the chase camera
  • if you use this constructor you have to attach the cam later to a spatial
  • doing spatial.addControl(chaseCamera);
  • @param cam the application camera

    */

    public ChaseCamera2(Camera cam) {

    this.cam = cam;

    initialUpVec = cam.getUp().clone();

    }



    /**
  • Constructs the chase camera, and registers inputs
  • if you use this constructor you have to attach the cam later to a spatial
  • doing spatial.addControl(chaseCamera);
  • @param cam the application camera
  • @param inputManager the inputManager of the application to register inputs

    */

    public ChaseCamera2(Camera cam, InputManager inputManager) {

    this(cam);

    registerWithInput(inputManager);

    }



    /**
  • Constructs the chase camera, and registers inputs
  • @param cam the application camera
  • @param target the spatial to follow
  • @param inputManager the inputManager of the application to register inputs

    */

    public ChaseCamera2(Camera cam, final Spatial target, InputManager inputManager) {

    this(cam, target);

    registerWithInput(inputManager);

    }



    /**
  • Constructs the physical chase camera, registers inputs and physics
  • @param cam the application camera
  • @param target the spatial to follow
  • @param inputManager the inputManager of the application to register inputs
  • @param physicsSpace the PhysicsSpace, the cam shall be active in

    */

    public ChaseCamera2(Camera cam, final Spatial target,

    InputManager inputManager, PhysicsSpace physicsSpace) {

    //chase cam constructor stuff

    this.target = target;

    this.cam = cam;

    initialUpVec = cam.getUp().clone();



    //add PhysicsNode to world physicsSpace

    myPhysicsSpace = physicsSpace;



    //chase cam stuff.

    computePosition();

    target.addControl(this);

    prevPos = new Vector3f(target.getWorldTranslation());

    cam.setLocation(pos);

    registerWithInput(inputManager);

    }





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

    if (dragToRotate) {

    if (name.equals(ChaseCamToggleRotate) && enabled) {

    if (keyPressed) {

    canRotate = true;

    inputManager.setCursorVisible(false);

    } else {

    canRotate = false;

    inputManager.setCursorVisible(true);

    }

    }

    }



    }

    private boolean zoomin;



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

    if (name.equals(ChaseCamMoveLeft)) {

    rotateCamera(-value);

    } else if (name.equals(ChaseCamMoveRight)) {

    rotateCamera(value);

    } else if (name.equals(ChaseCamUp)) {

    vRotateCamera(value);

    } else if (name.equals(ChaseCamDown)) {

    vRotateCamera(-value);

    } else if (name.equals(ChaseCamZoomIn)) {

    zoomCamera(-value);

    if (zoomin == false) {

    distanceLerpFactor = 0;

    }

    zoomin = true;

    } else if (name.equals(ChaseCamZoomOut)) {

    zoomCamera(+value);

    if (zoomin == true) {

    distanceLerpFactor = 0;

    }

    zoomin = false;

    }

    }



    /**
  • Registers inputs with the input manager
  • @param inputManager

    */

    public final void registerWithInput(InputManager inputManager) {



    String[] inputs = {ChaseCamToggleRotate,

    ChaseCamDown,

    ChaseCamUp,

    ChaseCamMoveLeft,

    ChaseCamMoveRight,

    ChaseCamZoomIn,

    ChaseCamZoomOut};



    this.inputManager = inputManager;

    if (!invertYaxis) {

    inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, true));

    inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, false));

    } else {

    inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, false));

    inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, true));

    }

    inputManager.addMapping(ChaseCamZoomIn, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));

    inputManager.addMapping(ChaseCamZoomOut, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));

    if(!invertXaxis){

    inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, true));

    inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, false));

    }else{

    inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, false));

    inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, true));

    }

    inputManager.addMapping(ChaseCamToggleRotate, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));

    inputManager.addMapping(ChaseCamToggleRotate, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));



    inputManager.addListener(this, inputs);

    }



    /**
  • Sets custom triggers for toggleing the rotation of the cam
  • deafult are
  • new MouseButtonTrigger(MouseInput.BUTTON_LEFT) left mouse button
  • new MouseButtonTrigger(MouseInput.BUTTON_RIGHT) right mouse button
  • @param triggers

    */

    public void setToggleRotationTrigger(Trigger… triggers) {

    inputManager.deleteMapping(ChaseCamToggleRotate);

    inputManager.addMapping(ChaseCamToggleRotate, triggers);

    inputManager.addListener(this, ChaseCamToggleRotate);

    }



    /**
  • Sets custom triggers for zomming in the cam
  • default is
  • new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true) mouse wheel up
  • @param triggers

    */

    public void setZoomInTrigger(Trigger… triggers) {

    inputManager.deleteMapping(ChaseCamZoomIn);

    inputManager.addMapping(ChaseCamZoomIn, triggers);

    inputManager.addListener(this, ChaseCamZoomIn);

    }



    /**
  • Sets custom triggers for zomming out the cam
  • default is
  • new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false) mouse wheel down
  • @param triggers

    /

    public void setZoomOutTrigger(Trigger… triggers) {

    inputManager.deleteMapping(ChaseCamZoomOut);

    inputManager.addMapping(ChaseCamZoomOut, triggers);

    inputManager.addListener(this, ChaseCamZoomOut);

    }



    private void computePosition() {



    float hDistance = (distance) * FastMath.sin((FastMath.PI / 2) - vRotation);

    pos.set(hDistance * FastMath.cos(rotation), (distance) * FastMath.sin(vRotation), hDistance * FastMath.sin(rotation));

    pos.addLocal(target.getWorldTranslation());



    hDistance = (maxDistanceByZoom) * FastMath.sin((FastMath.PI / 2) - vRotation);

    maxPos = new Vector3f(hDistance * FastMath.cos(rotation), (maxDistanceByZoom) * FastMath.sin(vRotation), hDistance * FastMath.sin(rotation));

    maxPos = maxPos.add(target.getWorldTranslation());

    this.collide();

    }



    //rotate the camera around the target on the horizontal plane

    private void rotateCamera(float value) {

    if (!canRotate || !enabled) {

    return;

    }

    rotating = true;

    targetRotation += value * rotationSpeed;





    }



    //move the camera toward or away the target

    private void zoomCamera(float value) {

    if (!enabled) {

    return;

    }



    zooming = true;

    //targetDistance += value * zoomSpeed;

    maxDistanceByZoom += value * zoomSpeed;

    /if (targetDistance > maxDistance) {

    targetDistance = maxDistance;

    }

    if (targetDistance < minDistance) {

    targetDistance = minDistance;

    }
    /



    if (maxDistanceByZoom > maxDistance) {

    maxDistanceByZoom = maxDistance;

    }

    if (maxDistanceByZoom < minDistance) {

    maxDistanceByZoom = minDistance;

    }



    if ((targetVRotation < minVerticalRotation) /
    && (targetDistance > (minDistance + 1.0f))/) {

    targetVRotation = minVerticalRotation;

    }



    }



    //rotate the camera around the target on the vertical plane

    private void vRotateCamera(float value) {

    if (!canRotate || !enabled) {

    return;

    }

    vRotating = true;

    targetVRotation += value * rotationSpeed;

    if (targetVRotation > maxVerticalRotation) {

    targetVRotation = maxVerticalRotation;

    }

    if ((targetVRotation < minVerticalRotation) /
    && (targetDistance > (minDistance + 1.0f))*/) {

    targetVRotation = minVerticalRotation;

    }

    }



    /**
  • Updates the camera, should only be called internally

    */

    protected void updateCamera(float tpf) {

    if (enabled) {

    targetLocation.set(target.getWorldTranslation()).addLocal(lookAtOffset);

    if (smoothMotion) {



    //computation of target direction

    targetDir.set(targetLocation).subtractLocal(prevPos);

    float dist = targetDir.length();



    //Low pass filtering on the target postition to avoid shaking when physics are enabled.

    if (offsetDistance < dist) {

    //target moves, start chasing.

    chasing = true;

    //target moves, start trailing if it has to.

    if (trailingEnabled) {

    trailing = true;

    }

    //target moves…

    targetMoves = true;

    } else {

    //if target was moving, we compute a slight offset in rotation to avoid a rought stop of the cam

    //We do not if the player is rotationg the cam

    if (targetMoves && !canRotate) {

    if (targetRotation - rotation > trailingRotationInertia) {

    targetRotation = rotation + trailingRotationInertia;

    } else if (targetRotation - rotation < -trailingRotationInertia) {

    targetRotation = rotation - trailingRotationInertia;

    }

    }

    //Target stops

    targetMoves = false;

    }



    //the user is rotating the cam by dragging the mouse

    if (canRotate) {

    //reseting the trailing lerp factor

    trailingLerpFactor = 0;

    //stop trailing user has the control

    trailing = false;

    }





    if (trailingEnabled && trailing) {

    if (targetMoves) {

    //computation if the inverted direction of the target

    Vector3f a = targetDir.negate().normalizeLocal();

    //the x unit vector

    Vector3f b = Vector3f.UNIT_X;

    //2d is good enough

    a.y = 0;

    //computation of the rotation angle between the x axis and the trail

    if (targetDir.z > 0) {

    targetRotation = FastMath.TWO_PI - FastMath.acos(a.dot(b));

    } else {

    targetRotation = FastMath.acos(a.dot(b));

    }

    if (targetRotation - rotation > FastMath.PI || targetRotation - rotation < -FastMath.PI) {

    targetRotation -= FastMath.TWO_PI;

    }



    //if there is an important change in the direction while trailing reset of the lerp factor to avoid jumpy movements

    if (targetRotation != previousTargetRotation && FastMath.abs(targetRotation - previousTargetRotation) > FastMath.PI / 8) {

    trailingLerpFactor = 0;

    }

    previousTargetRotation = targetRotation;

    }

    //computing lerp factor

    trailingLerpFactor = Math.min(trailingLerpFactor + tpf * tpf * trailingSensitivity, 1);

    //computing rotation by linear interpolation

    rotation = FastMath.interpolateLinear(trailingLerpFactor, rotation, targetRotation);



    //if the rotation is near the target rotation we’re good, that’s over

    if (targetRotation + 0.01f >= rotation && targetRotation - 0.01f <= rotation) {

    trailing = false;

    trailingLerpFactor = 0;

    }

    }



    //linear interpolation of the distance while chasing

    if (chasing) {

    distance = temp.set(targetLocation).subtractLocal(cam.getLocation()).length();

    distanceLerpFactor = Math.min(distanceLerpFactor + (tpf * tpf * chasingSensitivity * 0.05f), 1);

    distance = FastMath.interpolateLinear(distanceLerpFactor, distance, targetDistance);

    if (targetDistance + 0.01f >= distance && targetDistance - 0.01f <= distance) {

    distanceLerpFactor = 0;

    chasing = false;

    }

    }



    //linear interpolation of the distance while zooming

    if (zooming) {

    distanceLerpFactor = Math.min(distanceLerpFactor + (tpf * tpf * zoomSensitivity), 1);

    distance = FastMath.interpolateLinear(distanceLerpFactor, distance, targetDistance);

    if (targetDistance + 0.1f >= distance && targetDistance - 0.1f <= distance) {

    zooming = false;

    distanceLerpFactor = 0;

    }

    }



    //linear interpolation of the rotation while rotating horizontally

    if (rotating) {

    rotationLerpFactor = Math.min(rotationLerpFactor + tpf * tpf * rotationSensitivity, 1);

    rotation = FastMath.interpolateLinear(rotationLerpFactor, rotation, targetRotation);

    if (targetRotation + 0.01f >= rotation && targetRotation - 0.01f <= rotation) {

    rotating = false;

    rotationLerpFactor = 0;

    }

    }



    //linear interpolation of the rotation while rotating vertically

    if (vRotating) {

    vRotationLerpFactor = Math.min(vRotationLerpFactor + tpf * tpf * rotationSensitivity, 1);

    vRotation = FastMath.interpolateLinear(vRotationLerpFactor, vRotation, targetVRotation);

    if (targetVRotation + 0.01f >= vRotation && targetVRotation - 0.01f <= vRotation) {

    vRotating = false;

    vRotationLerpFactor = 0;

    }

    }

    //computing the position

    computePosition();

    //setting the position at last

    cam.setLocation(pos.addLocal(lookAtOffset));

    } else {

    //easy no smooth motion

    vRotation = targetVRotation;

    rotation = targetRotation;

    distance = targetDistance;

    computePosition();

    cam.setLocation(pos.addLocal(lookAtOffset));

    }

    //keeping track on the previous position of the target

    prevPos.set(targetLocation);



    //the cam looks at the target

    cam.lookAt(targetLocation, initialUpVec);



    }

    }



    /**
  • Return the enabled/disabled state of the camera
  • @return true if the camera is enabled

    */

    public boolean isEnabled() {

    return enabled;

    }



    /**
  • Enable or disable the camera
  • @param enabled true to enable

    */

    public void setEnabled(boolean enabled) {

    this.enabled = enabled;

    if (!enabled) {

    canRotate = false; // reset this flag in-case it was on before

    }

    }



    /**
  • Returns the max zoom distance of the camera (default is 40)
  • @return maxDistance

    */

    public float getMaxDistance() {

    return maxDistance;

    }



    /**
  • Sets the max zoom distance of the camera (default is 40)
  • @param maxDistance

    */

    public void setMaxDistance(float maxDistance) {

    this.maxDistance = maxDistance;

    }



    /**
  • Returns the min zoom distance of the camera (default is 1)
  • @return minDistance

    */

    public float getMinDistance() {

    return minDistance;

    }



    /**
  • Sets the min zoom distance of the camera (default is 1)
  • @return minDistance

    */

    public void setMinDistance(float minDistance) {

    this.minDistance = minDistance;

    }



    /**
  • clone this camera for a spatial
  • @param spatial
  • @return

    */

    public Control cloneForSpatial(Spatial spatial) {

    ChaseCamera2 cc = new ChaseCamera2(cam, spatial, inputManager);

    cc.setMaxDistance(getMaxDistance());

    cc.setMinDistance(getMinDistance());

    return cc;

    }



    /**
  • Sets the spacial for the camera control, should only be used internally
  • @param spatial

    */

    public void setSpatial(Spatial spatial) {

    target = spatial;

    if (spatial == null) {

    return;

    }

    computePosition();

    prevPos = new Vector3f(target.getWorldTranslation());

    cam.setLocation(pos);

    }



    /**
  • update the camera control, should on ly be used internally
  • @param tpf

    */

    public void update(float tpf) {

    updateCamera(tpf);

    }



    /**
  • renders the camera control, should on ly be used internally
  • @param rm
  • @param vp

    */

    public void render(RenderManager rm, ViewPort vp) {

    //nothing to render

    }



    /**
  • Write the camera
  • @param ex the exporter
  • @throws IOException

    */

    public void write(JmeExporter ex) throws IOException {

    OutputCapsule capsule = ex.getCapsule(this);

    capsule.write(maxDistance, "maxDistance", 40);

    capsule.write(minDistance, "minDistance", 1);

    }



    /**
  • Read the camera
  • @param im
  • @throws IOException

    */

    public void read(JmeImporter im) throws IOException {

    InputCapsule ic = im.getCapsule(this);

    maxDistance = ic.readFloat("maxDistance", 40);

    minDistance = ic.readFloat("minDistance", 1);

    }



    /**

    *
  • @deprecated use getMaxVerticalRotation()

    */

    @Deprecated

    public float getMaxHeight() {

    return getMaxVerticalRotation();

    }



    /**

    *
  • @deprecated use setMaxVerticalRotation()

    */

    @Deprecated

    public void setMaxHeight(float maxHeight) {

    setMaxVerticalRotation(maxHeight);

    }



    /**

    *
  • @deprecated use getMinVerticalRotation()

    */

    @Deprecated

    public float getMinHeight() {

    return getMinVerticalRotation();

    }



    /**

    *
  • @deprecated use setMinVerticalRotation()

    */

    @Deprecated

    public void setMinHeight(float minHeight) {

    setMinVerticalRotation(minHeight);

    }



    /**
  • returns the maximal vertical rotation angle of the camera around the target
  • @return

    */

    public float getMaxVerticalRotation() {

    return maxVerticalRotation;

    }



    /**
  • sets the maximal vertical rotation angle of the camera around the target default is Pi/2;
  • @param maxVerticalRotation

    */

    public void setMaxVerticalRotation(float maxVerticalRotation) {

    this.maxVerticalRotation = maxVerticalRotation;

    }



    /**
  • returns the minimal vertical rotation angle of the camera around the target
  • @return

    */

    public float getMinVerticalRotation() {

    return minVerticalRotation;

    }



    /**
  • sets the minimal vertical rotation angle of the camera around the target default is 0;
  • @param minHeight

    */

    public void setMinVerticalRotation(float minHeight) {

    this.minVerticalRotation = minHeight;

    }



    /**
  • returns true is smmoth motion is enabled for this chase camera
  • @return

    */

    public boolean isSmoothMotion() {

    return smoothMotion;

    }



    /**
  • Enables smooth motion for this chase camera
  • @param smoothMotion

    */

    public void setSmoothMotion(boolean smoothMotion) {

    this.smoothMotion = smoothMotion;

    }



    /**
  • returns the chasing sensitivity
  • @return

    */

    public float getChasingSensitivity() {

    return chasingSensitivity;

    }



    /**
  • Sets the chasing sensitivity, the lower the value the slower the camera will follow the target when it moves
  • @param chasingSensitivity

    */

    public void setChasingSensitivity(float chasingSensitivity) {

    this.chasingSensitivity = chasingSensitivity;

    }



    /**
  • Returns the rotation sensitivity
  • @return

    */

    public float getRotationSensitivity() {

    return rotationSensitivity;

    }



    /**
  • Sets the rotation sensitivity, the lower the value the slower the camera will rotates around the target when draging with the mouse
  • default is 5
  • @param rotationSensitivity

    */

    public void setRotationSensitivity(float rotationSensitivity) {

    this.rotationSensitivity = rotationSensitivity;

    }



    /**
  • returns true if the trailing is enabled
  • @return

    */

    public boolean isTrailingEnabled() {

    return trailingEnabled;

    }



    /**
  • Enable the camera trailing : The camera smoothly go in the targets trail when it moves.
  • @param trailingEnabled

    */

    public void setTrailingEnabled(boolean trailingEnabled) {

    this.trailingEnabled = trailingEnabled;

    }



    /**
  • returns the trailing rotation inertia
  • @return

    */

    public float getTrailingRotationInertia() {

    return trailingRotationInertia;

    }



    /**
  • Sets the trailing rotation inertia : default is 0.1. This prevent the camera to roughtly stop when the target stops moving
  • before the camera reached the trail position.
  • @param trailingRotationInertia

    */

    public void setTrailingRotationInertia(float trailingRotationInertia) {

    this.trailingRotationInertia = trailingRotationInertia;

    }



    /**
  • returns the trailing sensitivity
  • @return

    */

    public float getTrailingSensitivity() {

    return trailingSensitivity;

    }



    /**
  • Sets the trailing sensitivity, the lower the value, the slower the camera will go in the target trail when it moves.
  • default is 0.5;
  • @param trailingSensitivity

    */

    public void setTrailingSensitivity(float trailingSensitivity) {

    this.trailingSensitivity = trailingSensitivity;

    }



    /**
  • returns the zoom sensitivity
  • @return

    */

    public float getZoomSensitivity() {

    return zoomSensitivity;

    }



    /**
  • Sets the zoom sensitivity, the lower the value, the slower the camera will zoom in and out.
  • default is 5.
  • @param zoomSensitivity

    */

    public void setZoomSensitivity(float zoomSensitivity) {

    this.zoomSensitivity = zoomSensitivity;

    }



    /**
  • Sets the default distance at start of applicaiton
  • @param defaultDistance

    */

    public void setDefaultDistance(float defaultDistance) {

    distance = defaultDistance;

    targetDistance = distance;

    }



    /**
  • sets the default horizontal rotation of the camera at start of the application
  • @param angle

    */

    public void setDefaultHorizontalRotation(float angle) {

    rotation = angle;

    targetRotation = angle;

    }



    /**
  • sets the default vertical rotation of the camera at start of the application
  • @param angle

    */

    public void setDefaultVerticalRotation(float angle) {

    vRotation = angle;

    targetVRotation = angle;

    }



    /**
  • sets the current zoom distance for the chase camera
  • @param new distance

    */

    public void alterDistance(float alterBy) {

    this.zoomCamera(alterBy);

    }



    /**
  • @return If drag to rotate feature is enabled.

    *
  • @see FlyByCamera#setDragToRotate(boolean)

    */

    public boolean isDragToRotate() {

    return dragToRotate;

    }



    /**
  • @param dragToRotate When true, the user must hold the mouse button
  • and drag over the screen to rotate the camera, and the cursor is
  • visible until dragged. Otherwise, the cursor is invisible at all times
  • and holding the mouse button is not needed to rotate the camera.
  • This feature is disabled by default.

    */

    public void setDragToRotate(boolean dragToRotate) {

    this.dragToRotate = dragToRotate;

    this.canRotate = !dragToRotate;

    inputManager.setCursorVisible(dragToRotate);

    }



    /**
  • return the current distance from the camera to the target
  • @return

    */

    public float getDistanceToTarget() {

    return distance;

    }



    /**
  • returns the current horizontal rotation around the target in radians
  • @return

    */

    public float getHorizontalRotation() {

    return rotation;

    }



    /**
  • returns the current vertical rotation around the target in radians.
  • @return

    */

    public float getVerticalRotation() {

    return vRotation;

    }



    /**
  • returns the offset from the target’s position where the camera looks at
  • @return

    */

    public Vector3f getLookAtOffset() {

    return lookAtOffset;

    }



    /**
  • Sets the offset from the target’s position where the camera looks at
  • @param lookAtOffset

    */

    public void setLookAtOffset(Vector3f lookAtOffset) {

    this.lookAtOffset = lookAtOffset;

    }



    /**

    *
  • @param invertYaxis
  • @deprecated use setInvertVerticalAxis

    */

    @Deprecated

    public void setInvertYaxis(boolean invertYaxis) {

    setInvertVerticalAxis(invertYaxis);

    }



    /**
  • invert the vertical axis movement of the mouse
  • @param invertYaxis

    */

    public void setInvertVerticalAxis(boolean invertYaxis) {

    this.invertYaxis = invertYaxis;

    inputManager.deleteMapping(ChaseCamDown);

    inputManager.deleteMapping(ChaseCamUp);

    if (!invertYaxis) {

    inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, true));

    inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, false));

    } else {

    inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, false));

    inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, true));

    }

    inputManager.addListener(this, ChaseCamDown, ChaseCamUp);

    }



    /**
  • invert the Horizontal axis movement of the mouse
  • @param invertYaxis

    /

    public void setInvertHorizontalAxis(boolean invertXaxis) {

    this.invertXaxis = invertXaxis;

    inputManager.deleteMapping(ChaseCamMoveLeft);

    inputManager.deleteMapping(ChaseCamMoveRight);

    if(!invertXaxis){

    inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, true));

    inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, false));

    }else{

    inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, false));

    inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, true));

    }

    inputManager.addListener(this, ChaseCamMoveLeft, ChaseCamMoveRight);

    }



    public void collide() {

    LinkedList<PhysicsRayTestResult> testResults;

    testResults = (LinkedList) myPhysicsSpace.rayTest(target.getWorldTranslation(), maxPos.addLocal(lookAtOffset));

    float hitFraction = 1f;

    if(testResults != null && testResults.size() > 0) {

    hitFraction = testResults.getFirst().getHitFraction();

    }

    targetDistance = ((float)((int)(hitFraction
    100)))/100 * maxDistanceByZoom;

    }

    }

    [/java]



    note

    and my test case uncomment the code in the methods listed above and drag the mouse down to see the issue . my purpose you can freely look around with the mouse walk with RMB jump with MMB and Shoot with LMB in the camera direction



    [java]/*
  • Copyright © 2009-2010 jMonkeyEngine
  • 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 ‘jMonkeyEngine’ 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 hks.test;



    import com.jme3.animation.AnimChannel;

    import com.jme3.animation.AnimControl;

    import com.jme3.animation.AnimEventListener;

    import com.jme3.animation.LoopMode;

    import com.jme3.bullet.BulletAppState;

    import com.jme3.app.SimpleApplication;

    import com.jme3.bounding.BoundingBox;

    import com.jme3.bullet.PhysicsSpace;

    import com.jme3.bullet.collision.PhysicsCollisionEvent;

    import com.jme3.bullet.collision.PhysicsCollisionListener;

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

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

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

    import com.jme3.bullet.control.CharacterControl;

    import com.jme3.bullet.control.RigidBodyControl;

    import com.jme3.bullet.nodes.PhysicsNode;

    import com.jme3.bullet.util.CollisionShapeFactory;

    import com.jme3.effect.EmitterSphereShape;

    import com.jme3.effect.ParticleEmitter;

    import com.jme3.effect.ParticleMesh.Type;

    import hks.input.ChaseCamera2;

    //import com.jme3.input.ChaseCamera;

    import com.jme3.input.KeyInput;

    import com.jme3.input.MouseInput;

    import com.jme3.input.controls.ActionListener;

    import com.jme3.input.controls.KeyTrigger;

    import com.jme3.input.controls.MouseButtonTrigger;

    import com.jme3.light.DirectionalLight;

    import com.jme3.material.Material;

    import com.jme3.material.RenderState.BlendMode;

    import com.jme3.math.ColorRGBA;

    import com.jme3.math.FastMath;

    import com.jme3.math.Quaternion;

    import com.jme3.math.Vector2f;

    import com.jme3.math.Vector3f;

    import com.jme3.post.FilterPostProcessor;

    import com.jme3.post.filters.BloomFilter;

    import com.jme3.post.filters.CartoonEdgeFilter;

    import com.jme3.renderer.Camera;

    import com.jme3.renderer.Caps;

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

    import com.jme3.scene.Geometry;

    import com.jme3.scene.Node;

    import com.jme3.scene.Spatial;

    import com.jme3.scene.Spatial.CullHint;

    import com.jme3.scene.shape.Box;

    import com.jme3.scene.shape.Sphere;

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

    import com.jme3.terrain.geomipmap.TerrainLodControl;

    import com.jme3.terrain.geomipmap.TerrainQuad;

    import com.jme3.terrain.heightmap.AbstractHeightMap;

    import com.jme3.terrain.heightmap.ImageBasedHeightMap;

    import com.jme3.texture.Texture;

    import com.jme3.texture.Texture.WrapMode;

    import com.jme3.util.SkyFactory;

    import java.util.ArrayList;

    import java.util.List;



    import jme3test.bullet.BombControl;

    import jme3tools.converters.ImageToAwt;



    /**

    *
  • @author normenhansen

    /

    public class TestWalkingCharToon extends SimpleApplication implements ActionListener, PhysicsCollisionListener, AnimEventListener {



    private BulletAppState bulletAppState;

    static final Quaternion ROTATE_LEFT = new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y);

    //character

    CharacterControl character;

    Node model;

    //temp vectors

    Vector3f walkDirection = new Vector3f();

    Quaternion modelRotation = new Quaternion();

    Vector3f modelDirection = new Vector3f();

    Vector3f modelRight = new Vector3f();

    //terrain

    TerrainQuad terrain;

    RigidBodyControl terrainPhysicsNode;

    //Materials

    Material matRock;

    Material matWire;

    Material matBullet;

    //animation

    AnimChannel animationChannel;

    AnimChannel shootingChannel;

    AnimControl animationControl;

    float airTime = 0;

    //camera

    boolean left = false, right = false, up = false, down = false, shoot = false;

    ChaseCamera2 chaseCam;

    //bullet

    Sphere bullet;

    SphereCollisionShape bulletCollisionShape;

    //explosion

    ParticleEmitter effect;

    //brick wall

    Box brick;

    float bLength = 0.8f;

    float bWidth = 0.4f;

    float bHeight = 0.4f;

    FilterPostProcessor fpp;

    private float lastFire;



    public static void main(String[] args) {

    TestWalkingCharToon app = new TestWalkingCharToon();

    app.start();

    }



    @Override

    public void simpleInitApp() {

    bulletAppState = new BulletAppState();

    bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);

    stateManager.attach(bulletAppState);

    setupKeys();

    prepareBullet();

    prepareEffect();

    createLight();

    createSky();

    createTerrain();

    createWall();

    createCharacter();

    setupChaseCamera();

    setupAnimationController();

    setupFilter();

    setupFilters();

    makeToonish(rootNode);



    }



    private void setupFilter() {

    FilterPostProcessor fpp = new FilterPostProcessor(assetManager);

    BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);

    fpp.addFilter(bloom);

    viewPort.addProcessor(fpp);

    }



    private PhysicsSpace getPhysicsSpace() {

    return bulletAppState.getPhysicsSpace();

    }



    private void setupKeys() {

    inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));

    inputManager.addListener(this, "wireframe");

    inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A), new KeyTrigger(KeyInput.KEY_LEFT));

    inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D), new KeyTrigger(KeyInput.KEY_RIGHT));

    inputManager.addMapping("CharUp", new KeyTrigger(KeyInput.KEY_W),new MouseButtonTrigger(MouseInput.BUTTON_RIGHT), new KeyTrigger(KeyInput.KEY_UP));

    inputManager.addMapping("CharDown", new KeyTrigger(KeyInput.KEY_S), new KeyTrigger(KeyInput.KEY_DOWN));

    inputManager.addMapping("CharSpace", new KeyTrigger(KeyInput.KEY_RETURN), new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE));

    inputManager.addMapping("CharShoot", new KeyTrigger(KeyInput.KEY_SPACE), new MouseButtonTrigger(MouseInput.BUTTON_LEFT));



    inputManager.addListener(this, "CharLeft");

    inputManager.addListener(this, "CharRight");

    inputManager.addListener(this, "CharUp");

    inputManager.addListener(this, "CharDown");

    inputManager.addListener(this, "CharSpace");

    inputManager.addListener(this, "CharShoot");

    }



    private void createWall() {

    float xOff = -144;

    float zOff = -40;

    float startpt = bLength / 4 - xOff;

    float height = 6.1f;

    brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth);

    brick.scaleTextureCoordinates(new Vector2f(1f, .5f));

    for (int j = 0; j < 15; j++) {

    for (int i = 0; i < 4; i++) {

    Vector3f vt = new Vector3f(i * bLength * 2 + startpt, bHeight + height, zOff);

    addBrick(vt);

    }

    startpt = -startpt;

    height += 1.01f * bHeight;

    }

    }



    private void addBrick(Vector3f ori) {

    Geometry reBoxg = new Geometry("brick", brick);

    reBoxg.setMaterial(matRock);

    PhysicsNode brickNode = new PhysicsNode(reBoxg, new BoxCollisionShape(new Vector3f(bLength, bHeight, bWidth)), 1.5f);

    brickNode.setLocalTranslation(ori);

    brickNode.setMass(1000);

    brickNode.setFriction(100);

    brickNode.setGravity(new Vector3f(0,1875f,0));

    brickNode.setShadowMode(ShadowMode.CastAndReceive);

    this.rootNode.attachChild(brickNode);

    this.getPhysicsSpace().add(brickNode);

    }



    private void prepareBullet() {

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

    bullet.setTextureMode(TextureMode.Projected);

    bulletCollisionShape = new SphereCollisionShape(0.4f);

    matBullet = new Material(getAssetManager(), "Common/MatDefs/Misc/SolidColor.j3md");

    matBullet.setColor("Color", ColorRGBA.Green);

    matBullet.setColor("m_GlowColor", ColorRGBA.Green);

    getPhysicsSpace().addCollisionListener(this);

    }



    private void prepareEffect() {

    int COUNT_FACTOR = 1;

    float COUNT_FACTOR_F = 1f;

    effect = new ParticleEmitter("Flame", Type.Triangle, 32 * COUNT_FACTOR);

    effect.setSelectRandomImage(true);

    effect.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f, (float) (1f / COUNT_FACTOR_F)));

    effect.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));

    effect.setStartSize(1.3f);

    effect.setEndSize(2f);

    effect.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));

    effect.setParticlesPerSec(0);

    effect.setGravity(-5f);

    effect.setLowLife(.4f);

    effect.setHighLife(.5f);

    effect.setInitialVelocity(new Vector3f(0, 7, 0));

    effect.setVelocityVariation(1f);

    effect.setImagesX(2);

    effect.setImagesY(2);

    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");

    mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png"));

    effect.setMaterial(mat);

    effect.setLocalScale(100);

    rootNode.attachChild(effect);

    }



    private void createLight() {

    Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();

    DirectionalLight dl = new DirectionalLight();

    dl.setDirection(direction);

    dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));

    rootNode.addLight(dl);

    }



    private void createSky() {

    rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));

    }



    private void createTerrain() {

    matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");

    matRock.setBoolean("useTriPlanarMapping", false);

    matRock.setBoolean("WardIso", true);

    matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));

    Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");

    Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");

    grass.setWrap(WrapMode.Repeat);

    matRock.setTexture("DiffuseMap", grass);

    matRock.setFloat("DiffuseMap_0_scale", 64);

    Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");

    dirt.setWrap(WrapMode.Repeat);

    matRock.setTexture("DiffuseMap_1", dirt);

    matRock.setFloat("DiffuseMap_1_scale", 16);

    Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");

    rock.setWrap(WrapMode.Repeat);

    matRock.setTexture("DiffuseMap_2", rock);

    matRock.setFloat("DiffuseMap_2_scale", 128);

    Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.png");

    normalMap0.setWrap(WrapMode.Repeat);

    Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");

    normalMap1.setWrap(WrapMode.Repeat);

    Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");

    normalMap2.setWrap(WrapMode.Repeat);

    matRock.setTexture("NormalMap", normalMap0);

    matRock.setTexture("NormalMap_1", normalMap2);

    matRock.setTexture("NormalMap_2", normalMap2);

    matWire = new Material(assetManager, "Common/MatDefs/Misc/WireColor.j3md");

    matWire.setColor("Color", ColorRGBA.Green);



    AbstractHeightMap heightmap = null;

    try {

    heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 0.25f);

    heightmap.load();



    } catch (Exception e) {

    e.printStackTrace();

    }



    terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());

    List<Camera> cameras = new ArrayList<Camera>();

    cameras.add(getCamera());

    TerrainLodControl control = new TerrainLodControl(terrain, cameras);

    terrain.addControl(control);

    terrain.setMaterial(matRock);

    terrain.setModelBound(new BoundingBox());

    terrain.updateModelBound();

    terrain.setLocalScale(new Vector3f(2, 2, 2));



    terrainPhysicsNode = new RigidBodyControl(CollisionShapeFactory.createMeshShape(terrain), 0);

    terrain.addControl(terrainPhysicsNode);

    rootNode.attachChild(terrain);

    getPhysicsSpace().add(terrainPhysicsNode);

    }



    private void createCharacter() {

    CapsuleCollisionShape capsule = new CapsuleCollisionShape(0.90f, 3.5f);

    character = new CharacterControl(capsule, 0.01f);

    model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");

    model.setLocalScale(0.5f);

    model.addControl(character);

    character.setPhysicsLocation(new Vector3f(-140, 10, -10));

    character.attachDebugShape(assetManager);

    rootNode.attachChild(model);

    getPhysicsSpace().add(character);

    }



    private void setupChaseCamera() {

    flyCam.setEnabled(false);

    chaseCam = new ChaseCamera2(cam, model, inputManager, getPhysicsSpace());



    chaseCam.setTrailingEnabled(true);

    chaseCam.setDragToRotate(false);

    chaseCam.setMaxVerticalRotation(75.0f
    FastMath.DEG_TO_RAD);

    chaseCam.setMinVerticalRotation(-65FastMath.DEG_TO_RAD);

    chaseCam.setDefaultVerticalRotation(0f
    FastMath.DEG_TO_RAD);

    chaseCam.setDefaultDistance(15.5f);//15.5

    chaseCam.setMaxDistance(15.5f);//5.5 is minimum

    chaseCam.setMinDistance(8.5f);



    }



    private void setupAnimationController() {

    animationControl = model.getControl(AnimControl.class);

    animationControl.addListener(this);

    animationChannel = animationControl.createChannel();

    shootingChannel = animationControl.createChannel();

    shootingChannel.addBone(animationControl.getSkeleton().getBone("uparm.right"));

    shootingChannel.addBone(animationControl.getSkeleton().getBone("arm.right"));

    shootingChannel.addBone(animationControl.getSkeleton().getBone("hand.right"));

    }



    @Override

    public void simpleUpdate(float tpf) {

    Vector3f camY = new Vector3f(cam.getDirection().x,0,cam.getDirection().z);

    Vector3f camDir = camY.clone().normalize().multLocal(0.2f);

    Vector3f camLeft = cam.getLeft().clone().multLocal(0.2f);

    camDir.y = 0;

    camLeft.y = 0;

    walkDirection.set(0, 0, 0);

    modelDirection.set(0, 0, 2);

    if (left) {

    walkDirection.addLocal(camLeft);

    }

    if (right) {

    walkDirection.addLocal(camLeft.negate());

    }

    if (up) {

    walkDirection.addLocal(camDir);

    }

    if (down) {

    walkDirection.addLocal(camDir.negate());

    }

    if(shoot && (double)(timer.getTimeInSeconds() - lastFire) > 0.10000000000000001D){

    lastFire = timer.getTimeInSeconds();

    bulletControl();



    }

    if (!character.onGround()) {

    airTime = airTime + tpf;

    } else {

    airTime = 0;

    }

    if (walkDirection.length() == 0) {

    if (!"stand".equals(animationChannel.getAnimationName())) {

    animationChannel.setAnim("stand", 1f);

    }

    } else {

    modelRotation.lookAt(walkDirection, Vector3f.UNIT_Y);

    if (airTime > .3f) {

    if (!"stand".equals(animationChannel.getAnimationName())) {

    animationChannel.setAnim("stand");

    }

    } else if (!"Walk".equals(animationChannel.getAnimationName())) {

    animationChannel.setAnim("Walk", 0.7f);

    }

    }

    model.getChild(0).setLocalRotation(modelRotation);

    modelRotation.multLocal(modelDirection);

    modelRight.set(modelDirection);

    ROTATE_LEFT.multLocal(modelRight);

    character.setWalkDirection(walkDirection);

    modelRotation.lookAt(camDir, Vector3f.UNIT_Y);//my code

    chaseCam.setLookAtOffset(new Vector3f(0,model.getLocalScale().y+3 /* +terrain.getHeight(new Vector2f(cam.getLocation().x,cam.getLocation().y))*/,0));

    System.out.println(chaseCam.getDistanceToTarget());

    }



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

    if (binding.equals("CharLeft")) {

    if (value) {

    left = true;

    } else {

    left = false;

    }

    } else if (binding.equals("CharRight")) {

    if (value) {

    right = true;

    } else {

    right = false;

    }

    } else if (binding.equals("CharUp")) {

    if (value) {

    up = true;

    } else {

    up = false;

    }

    } else if (binding.equals("CharDown")) {

    if (value) {

    down = true;

    } else {

    down = false;

    }

    } else if (binding.equals("CharSpace")) {

    character.jump();

    } else if (binding.equals("CharShoot")){

    if(value){

    shoot=true;

    }

    else {

    shoot=false;

    }

    }

    }



    /private void bulletControl() {

    shootingChannel.setAnim("Dodge", 0.1f);

    shootingChannel.setLoopMode(LoopMode.DontLoop);

    Geometry bulletg = new Geometry("bullet", bullet);

    bulletg.setMaterial(matBullet);

    Vector3f camDir = cam.getDirection().clone().multLocal(0.2f);

    bulletg.setShadowMode(ShadowMode.CastAndReceive);

    bulletg.setLocalTranslation(character.getPhysicsLocation().add(camDir.mult(1.8f).addLocal(modelRight.mult(0.9f))));

    RigidBodyControl bulletControl = new RigidBodyControl(bulletCollisionShape, 1);

    bulletControl.setCcdMotionThreshold(0.1f);

    bulletControl.setLinearVelocity(camDir.mult(5000));//modelDirection.mult(250)

    bulletg.addControl(bulletControl);

    rootNode.attachChild(bulletg);

    getPhysicsSpace().add(bulletControl);

    }
    /



    /public void collision(PhysicsCollisionEvent event) {

    if ("bullet".equals(event.getNodeA().getName())) {

    final Spatial node = event.getNodeA();

    getPhysicsSpace().remove(node);

    node.removeFromParent();

    effect.killAllParticles();

    effect.setLocalTranslation(node.getLocalTranslation());

    effect.emitAllParticles();

    } else if ("bullet".equals(event.getNodeB().getName())) {

    final Spatial node = event.getNodeB();

    getPhysicsSpace().remove(node);

    node.removeFromParent();

    effect.killAllParticles();

    effect.setLocalTranslation(node.getLocalTranslation());

    effect.emitAllParticles();

    }

    }
    /



    private void bulletControl() {

    shootingChannel.setAnim("Dodge", 0.1f);

    shootingChannel.setLoopMode(LoopMode.DontLoop);

    Vector3f camDir = cam.getDirection().clone().multLocal(0.2f);

    Geometry bulletg = new Geometry("bullet", bullet);

    bulletg.setMaterial(matBullet);

    bulletg.setShadowMode(ShadowMode.CastAndReceive);

    bulletg.setLocalTranslation(character.getPhysicsLocation().add(camDir.mult(1.8f).addLocal(modelRight.mult(0.9f))));

    RigidBodyControl bulletControl = new BombControl(bulletCollisionShape, 250);

    bulletControl.setCcdMotionThreshold(0.1f);

    bulletControl.setLinearVelocity(camDir.mult(5000));

    bulletg.addControl(bulletControl);

    rootNode.attachChild(bulletg);

    getPhysicsSpace().add(bulletControl);

    }



    public void collision(PhysicsCollisionEvent event) {

    if (event.getObjectA() instanceof BombControl) {

    final Spatial node = event.getNodeA();

    effect.killAllParticles();

    effect.setLocalTranslation(node.getLocalTranslation());

    effect.emitAllParticles();

    } else if (event.getObjectB() instanceof BombControl) {

    final Spatial node = event.getNodeB();

    effect.killAllParticles();

    effect.setLocalTranslation(node.getLocalTranslation());

    effect.emitAllParticles();

    }

    }



    public void setupFilters(){

    if (renderer.getCaps().contains(Caps.GLSL100)){

    fpp=new FilterPostProcessor(assetManager);

    fpp.addFilter(new CartoonEdgeFilter());

    viewPort.addProcessor(fpp);

    }

    }



    public void makeToonish(Spatial spatial){

    if (spatial instanceof Node){

    Node n = (Node) spatial;

    for (Spatial child : n.getChildren())

    makeToonish(child);

    }else if (spatial instanceof Geometry){

    Geometry g = (Geometry) spatial;

    Material m = g.getMaterial();

    if (m.getMaterialDef().getName().equals("Phong Lighting")){

    Texture t = assetManager.loadTexture("Textures/ColorRamp/toon.png");

    // t.setMinFilter(Texture.MinFilter.NearestNoMipMaps);

    // t.setMagFilter(Texture.MagFilter.Nearest);

    m.setTexture("m_ColorRamp", t);

    m.setBoolean("m_UseMaterialColors", true);

    m.setColor("m_Specular", ColorRGBA.Black);

    m.setColor("m_Diffuse", ColorRGBA.White);

    m.setBoolean("m_VertexLighting", true);

    }

    }

    }



    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {

    if (channel == shootingChannel) {

    channel.setAnim("stand");

    }

    }



    public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {

    }

    }

    [/java]

Thanks i’m gonna look at it