Home | History | Annotate | Download | only in input
      1 /*
      2  * Copyright (c) 2009-2012 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 package com.jme3.input;
     33 
     34 import com.jme3.export.InputCapsule;
     35 import com.jme3.export.JmeExporter;
     36 import com.jme3.export.JmeImporter;
     37 import com.jme3.export.OutputCapsule;
     38 import com.jme3.input.controls.*;
     39 import com.jme3.math.FastMath;
     40 import com.jme3.math.Vector3f;
     41 import com.jme3.renderer.Camera;
     42 import com.jme3.renderer.RenderManager;
     43 import com.jme3.renderer.ViewPort;
     44 import com.jme3.scene.Spatial;
     45 import com.jme3.scene.control.Control;
     46 import java.io.IOException;
     47 
     48 /**
     49  * A camera that follows a spatial and can turn around it by dragging the mouse
     50  * @author nehon
     51  */
     52 public class ChaseCamera implements ActionListener, AnalogListener, Control {
     53 
     54     protected Spatial target = null;
     55     protected float minVerticalRotation = 0.00f;
     56     protected float maxVerticalRotation = FastMath.PI / 2;
     57     protected float minDistance = 1.0f;
     58     protected float maxDistance = 40.0f;
     59     protected float distance = 20;
     60     protected float zoomSpeed = 2f;
     61     protected float rotationSpeed = 1.0f;
     62     protected float rotation = 0;
     63     protected float trailingRotationInertia = 0.05f;
     64     protected float zoomSensitivity = 5f;
     65     protected float rotationSensitivity = 5f;
     66     protected float chasingSensitivity = 5f;
     67     protected float trailingSensitivity = 0.5f;
     68     protected float vRotation = FastMath.PI / 6;
     69     protected boolean smoothMotion = false;
     70     protected boolean trailingEnabled = true;
     71     protected float rotationLerpFactor = 0;
     72     protected float trailingLerpFactor = 0;
     73     protected boolean rotating = false;
     74     protected boolean vRotating = false;
     75     protected float targetRotation = rotation;
     76     protected InputManager inputManager;
     77     protected Vector3f initialUpVec;
     78     protected float targetVRotation = vRotation;
     79     protected float vRotationLerpFactor = 0;
     80     protected float targetDistance = distance;
     81     protected float distanceLerpFactor = 0;
     82     protected boolean zooming = false;
     83     protected boolean trailing = false;
     84     protected boolean chasing = false;
     85     protected boolean canRotate;
     86     protected float offsetDistance = 0.002f;
     87     protected Vector3f prevPos;
     88     protected boolean targetMoves = false;
     89     protected boolean enabled = true;
     90     protected Camera cam = null;
     91     protected final Vector3f targetDir = new Vector3f();
     92     protected float previousTargetRotation;
     93     protected final Vector3f pos = new Vector3f();
     94     protected Vector3f targetLocation = new Vector3f(0, 0, 0);
     95     protected boolean dragToRotate = true;
     96     protected Vector3f lookAtOffset = new Vector3f(0, 0, 0);
     97     protected boolean leftClickRotate = true;
     98     protected boolean rightClickRotate = true;
     99     protected Vector3f temp = new Vector3f(0, 0, 0);
    100     protected boolean invertYaxis = false;
    101     protected boolean invertXaxis = false;
    102     protected final static String ChaseCamDown = "ChaseCamDown";
    103     protected final static String ChaseCamUp = "ChaseCamUp";
    104     protected final static String ChaseCamZoomIn = "ChaseCamZoomIn";
    105     protected final static String ChaseCamZoomOut = "ChaseCamZoomOut";
    106     protected final static String ChaseCamMoveLeft = "ChaseCamMoveLeft";
    107     protected final static String ChaseCamMoveRight = "ChaseCamMoveRight";
    108     protected final static String ChaseCamToggleRotate = "ChaseCamToggleRotate";
    109 
    110     /**
    111      * Constructs the chase camera
    112      * @param cam the application camera
    113      * @param target the spatial to follow
    114      */
    115     public ChaseCamera(Camera cam, final Spatial target) {
    116         this(cam);
    117         target.addControl(this);
    118     }
    119 
    120     /**
    121      * Constructs the chase camera
    122      * if you use this constructor you have to attach the cam later to a spatial
    123      * doing spatial.addControl(chaseCamera);
    124      * @param cam the application camera
    125      */
    126     public ChaseCamera(Camera cam) {
    127         this.cam = cam;
    128         initialUpVec = cam.getUp().clone();
    129     }
    130 
    131     /**
    132      * Constructs the chase camera, and registers inputs
    133      * if you use this constructor you have to attach the cam later to a spatial
    134      * doing spatial.addControl(chaseCamera);
    135      * @param cam the application camera
    136      * @param inputManager the inputManager of the application to register inputs
    137      */
    138     public ChaseCamera(Camera cam, InputManager inputManager) {
    139         this(cam);
    140         registerWithInput(inputManager);
    141     }
    142 
    143     /**
    144      * Constructs the chase camera, and registers inputs
    145      * @param cam the application camera
    146      * @param target the spatial to follow
    147      * @param inputManager the inputManager of the application to register inputs
    148      */
    149     public ChaseCamera(Camera cam, final Spatial target, InputManager inputManager) {
    150         this(cam, target);
    151         registerWithInput(inputManager);
    152     }
    153 
    154     public void onAction(String name, boolean keyPressed, float tpf) {
    155         if (dragToRotate) {
    156             if (name.equals(ChaseCamToggleRotate) && enabled) {
    157                 if (keyPressed) {
    158                     canRotate = true;
    159                     inputManager.setCursorVisible(false);
    160                 } else {
    161                     canRotate = false;
    162                     inputManager.setCursorVisible(true);
    163                 }
    164             }
    165         }
    166 
    167     }
    168     private boolean zoomin;
    169 
    170     public void onAnalog(String name, float value, float tpf) {
    171         if (name.equals(ChaseCamMoveLeft)) {
    172             rotateCamera(-value);
    173         } else if (name.equals(ChaseCamMoveRight)) {
    174             rotateCamera(value);
    175         } else if (name.equals(ChaseCamUp)) {
    176             vRotateCamera(value);
    177         } else if (name.equals(ChaseCamDown)) {
    178             vRotateCamera(-value);
    179         } else if (name.equals(ChaseCamZoomIn)) {
    180             zoomCamera(-value);
    181             if (zoomin == false) {
    182                 distanceLerpFactor = 0;
    183             }
    184             zoomin = true;
    185         } else if (name.equals(ChaseCamZoomOut)) {
    186             zoomCamera(+value);
    187             if (zoomin == true) {
    188                 distanceLerpFactor = 0;
    189             }
    190             zoomin = false;
    191         }
    192     }
    193 
    194     /**
    195      * Registers inputs with the input manager
    196      * @param inputManager
    197      */
    198     public final void registerWithInput(InputManager inputManager) {
    199 
    200         String[] inputs = {ChaseCamToggleRotate,
    201             ChaseCamDown,
    202             ChaseCamUp,
    203             ChaseCamMoveLeft,
    204             ChaseCamMoveRight,
    205             ChaseCamZoomIn,
    206             ChaseCamZoomOut};
    207 
    208         this.inputManager = inputManager;
    209         if (!invertYaxis) {
    210             inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
    211             inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
    212         } else {
    213             inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
    214             inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
    215         }
    216         inputManager.addMapping(ChaseCamZoomIn, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
    217         inputManager.addMapping(ChaseCamZoomOut, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));
    218         if(!invertXaxis){
    219             inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, true));
    220             inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, false));
    221         }else{
    222             inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, false));
    223             inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, true));
    224         }
    225         inputManager.addMapping(ChaseCamToggleRotate, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
    226         inputManager.addMapping(ChaseCamToggleRotate, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
    227 
    228         inputManager.addListener(this, inputs);
    229     }
    230 
    231     /**
    232      * Sets custom triggers for toggleing the rotation of the cam
    233      * deafult are
    234      * new MouseButtonTrigger(MouseInput.BUTTON_LEFT)  left mouse button
    235      * new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)  right mouse button
    236      * @param triggers
    237      */
    238     public void setToggleRotationTrigger(Trigger... triggers) {
    239         inputManager.deleteMapping(ChaseCamToggleRotate);
    240         inputManager.addMapping(ChaseCamToggleRotate, triggers);
    241         inputManager.addListener(this, ChaseCamToggleRotate);
    242     }
    243 
    244     /**
    245      * Sets custom triggers for zomming in the cam
    246      * default is
    247      * new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)  mouse wheel up
    248      * @param triggers
    249      */
    250     public void setZoomInTrigger(Trigger... triggers) {
    251         inputManager.deleteMapping(ChaseCamZoomIn);
    252         inputManager.addMapping(ChaseCamZoomIn, triggers);
    253         inputManager.addListener(this, ChaseCamZoomIn);
    254     }
    255 
    256     /**
    257      * Sets custom triggers for zomming out the cam
    258      * default is
    259      * new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false)  mouse wheel down
    260      * @param triggers
    261      */
    262     public void setZoomOutTrigger(Trigger... triggers) {
    263         inputManager.deleteMapping(ChaseCamZoomOut);
    264         inputManager.addMapping(ChaseCamZoomOut, triggers);
    265         inputManager.addListener(this, ChaseCamZoomOut);
    266     }
    267 
    268     private void computePosition() {
    269 
    270         float hDistance = (distance) * FastMath.sin((FastMath.PI / 2) - vRotation);
    271         pos.set(hDistance * FastMath.cos(rotation), (distance) * FastMath.sin(vRotation), hDistance * FastMath.sin(rotation));
    272         pos.addLocal(target.getWorldTranslation());
    273     }
    274 
    275     //rotate the camera around the target on the horizontal plane
    276     private void rotateCamera(float value) {
    277         if (!canRotate || !enabled) {
    278             return;
    279         }
    280         rotating = true;
    281         targetRotation += value * rotationSpeed;
    282 
    283 
    284     }
    285 
    286     //move the camera toward or away the target
    287     private void zoomCamera(float value) {
    288         if (!enabled) {
    289             return;
    290         }
    291 
    292         zooming = true;
    293         targetDistance += value * zoomSpeed;
    294         if (targetDistance > maxDistance) {
    295             targetDistance = maxDistance;
    296         }
    297         if (targetDistance < minDistance) {
    298             targetDistance = minDistance;
    299         }
    300         if ((targetVRotation < minVerticalRotation) && (targetDistance > (minDistance + 1.0f))) {
    301             targetVRotation = minVerticalRotation;
    302         }
    303     }
    304 
    305     //rotate the camera around the target on the vertical plane
    306     private void vRotateCamera(float value) {
    307         if (!canRotate || !enabled) {
    308             return;
    309         }
    310         vRotating = true;
    311         targetVRotation += value * rotationSpeed;
    312         if (targetVRotation > maxVerticalRotation) {
    313             targetVRotation = maxVerticalRotation;
    314         }
    315         if ((targetVRotation < minVerticalRotation) && (targetDistance > (minDistance + 1.0f))) {
    316             targetVRotation = minVerticalRotation;
    317         }
    318     }
    319 
    320     /**
    321      * Updates the camera, should only be called internally
    322      */
    323     protected void updateCamera(float tpf) {
    324         if (enabled) {
    325             targetLocation.set(target.getWorldTranslation()).addLocal(lookAtOffset);
    326             if (smoothMotion) {
    327 
    328                 //computation of target direction
    329                 targetDir.set(targetLocation).subtractLocal(prevPos);
    330                 float dist = targetDir.length();
    331 
    332                 //Low pass filtering on the target postition to avoid shaking when physics are enabled.
    333                 if (offsetDistance < dist) {
    334                     //target moves, start chasing.
    335                     chasing = true;
    336                     //target moves, start trailing if it has to.
    337                     if (trailingEnabled) {
    338                         trailing = true;
    339                     }
    340                     //target moves...
    341                     targetMoves = true;
    342                 } else {
    343                     //if target was moving, we compute a slight offset in rotation to avoid a rought stop of the cam
    344                     //We do not if the player is rotationg the cam
    345                     if (targetMoves && !canRotate) {
    346                         if (targetRotation - rotation > trailingRotationInertia) {
    347                             targetRotation = rotation + trailingRotationInertia;
    348                         } else if (targetRotation - rotation < -trailingRotationInertia) {
    349                             targetRotation = rotation - trailingRotationInertia;
    350                         }
    351                     }
    352                     //Target stops
    353                     targetMoves = false;
    354                 }
    355 
    356                 //the user is rotating the cam by dragging the mouse
    357                 if (canRotate) {
    358                     //reseting the trailing lerp factor
    359                     trailingLerpFactor = 0;
    360                     //stop trailing user has the control
    361                     trailing = false;
    362                 }
    363 
    364 
    365                 if (trailingEnabled && trailing) {
    366                     if (targetMoves) {
    367                         //computation if the inverted direction of the target
    368                         Vector3f a = targetDir.negate().normalizeLocal();
    369                         //the x unit vector
    370                         Vector3f b = Vector3f.UNIT_X;
    371                         //2d is good enough
    372                         a.y = 0;
    373                         //computation of the rotation angle between the x axis and the trail
    374                         if (targetDir.z > 0) {
    375                             targetRotation = FastMath.TWO_PI - FastMath.acos(a.dot(b));
    376                         } else {
    377                             targetRotation = FastMath.acos(a.dot(b));
    378                         }
    379                         if (targetRotation - rotation > FastMath.PI || targetRotation - rotation < -FastMath.PI) {
    380                             targetRotation -= FastMath.TWO_PI;
    381                         }
    382 
    383                         //if there is an important change in the direction while trailing reset of the lerp factor to avoid jumpy movements
    384                         if (targetRotation != previousTargetRotation && FastMath.abs(targetRotation - previousTargetRotation) > FastMath.PI / 8) {
    385                             trailingLerpFactor = 0;
    386                         }
    387                         previousTargetRotation = targetRotation;
    388                     }
    389                     //computing lerp factor
    390                     trailingLerpFactor = Math.min(trailingLerpFactor + tpf * tpf * trailingSensitivity, 1);
    391                     //computing rotation by linear interpolation
    392                     rotation = FastMath.interpolateLinear(trailingLerpFactor, rotation, targetRotation);
    393 
    394                     //if the rotation is near the target rotation we're good, that's over
    395                     if (targetRotation + 0.01f >= rotation && targetRotation - 0.01f <= rotation) {
    396                         trailing = false;
    397                         trailingLerpFactor = 0;
    398                     }
    399                 }
    400 
    401                 //linear interpolation of the distance while chasing
    402                 if (chasing) {
    403                     distance = temp.set(targetLocation).subtractLocal(cam.getLocation()).length();
    404                     distanceLerpFactor = Math.min(distanceLerpFactor + (tpf * tpf * chasingSensitivity * 0.05f), 1);
    405                     distance = FastMath.interpolateLinear(distanceLerpFactor, distance, targetDistance);
    406                     if (targetDistance + 0.01f >= distance && targetDistance - 0.01f <= distance) {
    407                         distanceLerpFactor = 0;
    408                         chasing = false;
    409                     }
    410                 }
    411 
    412                 //linear interpolation of the distance while zooming
    413                 if (zooming) {
    414                     distanceLerpFactor = Math.min(distanceLerpFactor + (tpf * tpf * zoomSensitivity), 1);
    415                     distance = FastMath.interpolateLinear(distanceLerpFactor, distance, targetDistance);
    416                     if (targetDistance + 0.1f >= distance && targetDistance - 0.1f <= distance) {
    417                         zooming = false;
    418                         distanceLerpFactor = 0;
    419                     }
    420                 }
    421 
    422                 //linear interpolation of the rotation while rotating horizontally
    423                 if (rotating) {
    424                     rotationLerpFactor = Math.min(rotationLerpFactor + tpf * tpf * rotationSensitivity, 1);
    425                     rotation = FastMath.interpolateLinear(rotationLerpFactor, rotation, targetRotation);
    426                     if (targetRotation + 0.01f >= rotation && targetRotation - 0.01f <= rotation) {
    427                         rotating = false;
    428                         rotationLerpFactor = 0;
    429                     }
    430                 }
    431 
    432                 //linear interpolation of the rotation while rotating vertically
    433                 if (vRotating) {
    434                     vRotationLerpFactor = Math.min(vRotationLerpFactor + tpf * tpf * rotationSensitivity, 1);
    435                     vRotation = FastMath.interpolateLinear(vRotationLerpFactor, vRotation, targetVRotation);
    436                     if (targetVRotation + 0.01f >= vRotation && targetVRotation - 0.01f <= vRotation) {
    437                         vRotating = false;
    438                         vRotationLerpFactor = 0;
    439                     }
    440                 }
    441                 //computing the position
    442                 computePosition();
    443                 //setting the position at last
    444                 cam.setLocation(pos.addLocal(lookAtOffset));
    445             } else {
    446                 //easy no smooth motion
    447                 vRotation = targetVRotation;
    448                 rotation = targetRotation;
    449                 distance = targetDistance;
    450                 computePosition();
    451                 cam.setLocation(pos.addLocal(lookAtOffset));
    452             }
    453             //keeping track on the previous position of the target
    454             prevPos.set(targetLocation);
    455 
    456             //the cam looks at the target
    457             cam.lookAt(targetLocation, initialUpVec);
    458 
    459         }
    460     }
    461 
    462     /**
    463      * Return the enabled/disabled state of the camera
    464      * @return true if the camera is enabled
    465      */
    466     public boolean isEnabled() {
    467         return enabled;
    468     }
    469 
    470     /**
    471      * Enable or disable the camera
    472      * @param enabled true to enable
    473      */
    474     public void setEnabled(boolean enabled) {
    475         this.enabled = enabled;
    476         if (!enabled) {
    477             canRotate = false; // reset this flag in-case it was on before
    478         }
    479     }
    480 
    481     /**
    482      * Returns the max zoom distance of the camera (default is 40)
    483      * @return maxDistance
    484      */
    485     public float getMaxDistance() {
    486         return maxDistance;
    487     }
    488 
    489     /**
    490      * Sets the max zoom distance of the camera (default is 40)
    491      * @param maxDistance
    492      */
    493     public void setMaxDistance(float maxDistance) {
    494         this.maxDistance = maxDistance;
    495     }
    496 
    497     /**
    498      * Returns the min zoom distance of the camera (default is 1)
    499      * @return minDistance
    500      */
    501     public float getMinDistance() {
    502         return minDistance;
    503     }
    504 
    505     /**
    506      * Sets the min zoom distance of the camera (default is 1)
    507      * @return minDistance
    508      */
    509     public void setMinDistance(float minDistance) {
    510         this.minDistance = minDistance;
    511     }
    512 
    513     /**
    514      * clone this camera for a spatial
    515      * @param spatial
    516      * @return
    517      */
    518     public Control cloneForSpatial(Spatial spatial) {
    519         ChaseCamera cc = new ChaseCamera(cam, spatial, inputManager);
    520         cc.setMaxDistance(getMaxDistance());
    521         cc.setMinDistance(getMinDistance());
    522         return cc;
    523     }
    524 
    525     /**
    526      * Sets the spacial for the camera control, should only be used internally
    527      * @param spatial
    528      */
    529     public void setSpatial(Spatial spatial) {
    530         target = spatial;
    531         if (spatial == null) {
    532             return;
    533         }
    534         computePosition();
    535         prevPos = new Vector3f(target.getWorldTranslation());
    536         cam.setLocation(pos);
    537     }
    538 
    539     /**
    540      * update the camera control, should only be used internally
    541      * @param tpf
    542      */
    543     public void update(float tpf) {
    544         updateCamera(tpf);
    545     }
    546 
    547     /**
    548      * renders the camera control, should only be used internally
    549      * @param rm
    550      * @param vp
    551      */
    552     public void render(RenderManager rm, ViewPort vp) {
    553         //nothing to render
    554     }
    555 
    556     /**
    557      * Write the camera
    558      * @param ex the exporter
    559      * @throws IOException
    560      */
    561     public void write(JmeExporter ex) throws IOException {
    562         OutputCapsule capsule = ex.getCapsule(this);
    563         capsule.write(maxDistance, "maxDistance", 40);
    564         capsule.write(minDistance, "minDistance", 1);
    565     }
    566 
    567     /**
    568      * Read the camera
    569      * @param im
    570      * @throws IOException
    571      */
    572     public void read(JmeImporter im) throws IOException {
    573         InputCapsule ic = im.getCapsule(this);
    574         maxDistance = ic.readFloat("maxDistance", 40);
    575         minDistance = ic.readFloat("minDistance", 1);
    576     }
    577 
    578     /**
    579      * returns the maximal vertical rotation angle of the camera around the target
    580      * @return
    581      */
    582     public float getMaxVerticalRotation() {
    583         return maxVerticalRotation;
    584     }
    585 
    586     /**
    587      * sets the maximal vertical rotation angle of the camera around the target default is Pi/2;
    588      * @param maxVerticalRotation
    589      */
    590     public void setMaxVerticalRotation(float maxVerticalRotation) {
    591         this.maxVerticalRotation = maxVerticalRotation;
    592     }
    593 
    594     /**
    595      * returns the minimal vertical rotation angle of the camera around the target
    596      * @return
    597      */
    598     public float getMinVerticalRotation() {
    599         return minVerticalRotation;
    600     }
    601 
    602     /**
    603      * sets the minimal vertical rotation angle of the camera around the target default is 0;
    604      * @param minHeight
    605      */
    606     public void setMinVerticalRotation(float minHeight) {
    607         this.minVerticalRotation = minHeight;
    608     }
    609 
    610     /**
    611      * returns true is smmoth motion is enabled for this chase camera
    612      * @return
    613      */
    614     public boolean isSmoothMotion() {
    615         return smoothMotion;
    616     }
    617 
    618     /**
    619      * Enables smooth motion for this chase camera
    620      * @param smoothMotion
    621      */
    622     public void setSmoothMotion(boolean smoothMotion) {
    623         this.smoothMotion = smoothMotion;
    624     }
    625 
    626     /**
    627      * returns the chasing sensitivity
    628      * @return
    629      */
    630     public float getChasingSensitivity() {
    631         return chasingSensitivity;
    632     }
    633 
    634     /**
    635      *
    636      * Sets the chasing sensitivity, the lower the value the slower the camera will follow the target when it moves
    637      * default is 5
    638      * Only has an effect if smoothMotion is set to true and trailing is enabled
    639      * @param chasingSensitivity
    640      */
    641     public void setChasingSensitivity(float chasingSensitivity) {
    642         this.chasingSensitivity = chasingSensitivity;
    643     }
    644 
    645     /**
    646      * Returns the rotation sensitivity
    647      * @return
    648      */
    649     public float getRotationSensitivity() {
    650         return rotationSensitivity;
    651     }
    652 
    653     /**
    654      * Sets the rotation sensitivity, the lower the value the slower the camera will rotates around the target when draging with the mouse
    655      * default is 5, values over 5 should have no effect.
    656      * If you want a significant slow down try values below 1.
    657      * Only has an effect if smoothMotion is set to true
    658      * @param rotationSensitivity
    659      */
    660     public void setRotationSensitivity(float rotationSensitivity) {
    661         this.rotationSensitivity = rotationSensitivity;
    662     }
    663 
    664     /**
    665      * returns true if the trailing is enabled
    666      * @return
    667      */
    668     public boolean isTrailingEnabled() {
    669         return trailingEnabled;
    670     }
    671 
    672     /**
    673      * Enable the camera trailing : The camera smoothly go in the targets trail when it moves.
    674      * Only has an effect if smoothMotion is set to true
    675      * @param trailingEnabled
    676      */
    677     public void setTrailingEnabled(boolean trailingEnabled) {
    678         this.trailingEnabled = trailingEnabled;
    679     }
    680 
    681     /**
    682      *
    683      * returns the trailing rotation inertia
    684      * @return
    685      */
    686     public float getTrailingRotationInertia() {
    687         return trailingRotationInertia;
    688     }
    689 
    690     /**
    691      * Sets the trailing rotation inertia : default is 0.1. This prevent the camera to roughtly stop when the target stops moving
    692      * before the camera reached the trail position.
    693      * Only has an effect if smoothMotion is set to true and trailing is enabled
    694      * @param trailingRotationInertia
    695      */
    696     public void setTrailingRotationInertia(float trailingRotationInertia) {
    697         this.trailingRotationInertia = trailingRotationInertia;
    698     }
    699 
    700     /**
    701      * returns the trailing sensitivity
    702      * @return
    703      */
    704     public float getTrailingSensitivity() {
    705         return trailingSensitivity;
    706     }
    707 
    708     /**
    709      * Only has an effect if smoothMotion is set to true and trailing is enabled
    710      * Sets the trailing sensitivity, the lower the value, the slower the camera will go in the target trail when it moves.
    711      * default is 0.5;
    712      * @param trailingSensitivity
    713      */
    714     public void setTrailingSensitivity(float trailingSensitivity) {
    715         this.trailingSensitivity = trailingSensitivity;
    716     }
    717 
    718     /**
    719      * returns the zoom sensitivity
    720      * @return
    721      */
    722     public float getZoomSensitivity() {
    723         return zoomSensitivity;
    724     }
    725 
    726     /**
    727      * Sets the zoom sensitivity, the lower the value, the slower the camera will zoom in and out.
    728      * default is 5.
    729      * @param zoomSensitivity
    730      */
    731     public void setZoomSensitivity(float zoomSensitivity) {
    732         this.zoomSensitivity = zoomSensitivity;
    733     }
    734 
    735     /**
    736      * Sets the default distance at start of applicaiton
    737      * @param defaultDistance
    738      */
    739     public void setDefaultDistance(float defaultDistance) {
    740         distance = defaultDistance;
    741         targetDistance = distance;
    742     }
    743 
    744     /**
    745      * sets the default horizontal rotation of the camera at start of the application
    746      * @param angle
    747      */
    748     public void setDefaultHorizontalRotation(float angle) {
    749         rotation = angle;
    750         targetRotation = angle;
    751     }
    752 
    753     /**
    754      * sets the default vertical rotation of the camera at start of the application
    755      * @param angle
    756      */
    757     public void setDefaultVerticalRotation(float angle) {
    758         vRotation = angle;
    759         targetVRotation = angle;
    760     }
    761 
    762     /**
    763      * @return If drag to rotate feature is enabled.
    764      *
    765      * @see FlyByCamera#setDragToRotate(boolean)
    766      */
    767     public boolean isDragToRotate() {
    768         return dragToRotate;
    769     }
    770 
    771     /**
    772      * @param dragToRotate When true, the user must hold the mouse button
    773      * and drag over the screen to rotate the camera, and the cursor is
    774      * visible until dragged. Otherwise, the cursor is invisible at all times
    775      * and holding the mouse button is not needed to rotate the camera.
    776      * This feature is disabled by default.
    777      */
    778     public void setDragToRotate(boolean dragToRotate) {
    779         this.dragToRotate = dragToRotate;
    780         this.canRotate = !dragToRotate;
    781         inputManager.setCursorVisible(dragToRotate);
    782     }
    783 
    784     /**
    785      * return the current distance from the camera to the target
    786      * @return
    787      */
    788     public float getDistanceToTarget() {
    789         return distance;
    790     }
    791 
    792     /**
    793      * returns the current horizontal rotation around the target in radians
    794      * @return
    795      */
    796     public float getHorizontalRotation() {
    797         return rotation;
    798     }
    799 
    800     /**
    801      * returns the current vertical rotation around the target in radians.
    802      * @return
    803      */
    804     public float getVerticalRotation() {
    805         return vRotation;
    806     }
    807 
    808     /**
    809      * returns the offset from the target's position where the camera looks at
    810      * @return
    811      */
    812     public Vector3f getLookAtOffset() {
    813         return lookAtOffset;
    814     }
    815 
    816     /**
    817      * Sets the offset from the target's position where the camera looks at
    818      * @param lookAtOffset
    819      */
    820     public void setLookAtOffset(Vector3f lookAtOffset) {
    821         this.lookAtOffset = lookAtOffset;
    822     }
    823 
    824     /**
    825      * Sets the up vector of the camera used for the lookAt on the target
    826      * @param up
    827      */
    828     public void setUpVector(Vector3f up){
    829         initialUpVec=up;
    830     }
    831 
    832     /**
    833      * Returns the up vector of the camera used for the lookAt on the target
    834      * @return
    835      */
    836     public Vector3f getUpVector(){
    837         return initialUpVec;
    838     }
    839 
    840     /**
    841      * invert the vertical axis movement of the mouse
    842      * @param invertYaxis
    843      */
    844     public void setInvertVerticalAxis(boolean invertYaxis) {
    845         this.invertYaxis = invertYaxis;
    846         inputManager.deleteMapping(ChaseCamDown);
    847         inputManager.deleteMapping(ChaseCamUp);
    848         if (!invertYaxis) {
    849             inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
    850             inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
    851         } else {
    852             inputManager.addMapping(ChaseCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, false));
    853             inputManager.addMapping(ChaseCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, true));
    854         }
    855         inputManager.addListener(this, ChaseCamDown, ChaseCamUp);
    856     }
    857 
    858     /**
    859      * invert the Horizontal axis movement of the mouse
    860      * @param invertXaxis
    861      */
    862     public void setInvertHorizontalAxis(boolean invertXaxis) {
    863         this.invertXaxis = invertXaxis;
    864         inputManager.deleteMapping(ChaseCamMoveLeft);
    865         inputManager.deleteMapping(ChaseCamMoveRight);
    866         if(!invertXaxis){
    867             inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, true));
    868             inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, false));
    869         }else{
    870             inputManager.addMapping(ChaseCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, false));
    871             inputManager.addMapping(ChaseCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, true));
    872         }
    873         inputManager.addListener(this, ChaseCamMoveLeft, ChaseCamMoveRight);
    874     }
    875 }
    876