Home | History | Annotate | Download | only in renderer
      1 /*
      2  * Copyright (c) 2009-2010 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.renderer;
     33 
     34 import com.jme3.bounding.BoundingBox;
     35 import com.jme3.bounding.BoundingVolume;
     36 import com.jme3.export.*;
     37 import com.jme3.math.*;
     38 import com.jme3.util.TempVars;
     39 import java.io.IOException;
     40 import java.util.logging.Level;
     41 import java.util.logging.Logger;
     42 
     43 /**
     44  * <code>Camera</code> is a standalone, purely mathematical class for doing
     45  * camera-related computations.
     46  *
     47  * <p>
     48  * Given input data such as location, orientation (direction, left, up),
     49  * and viewport settings, it can compute data necessary to render objects
     50  * with the graphics library. Two matrices are generated, the view matrix
     51  * transforms objects from world space into eye space, while the projection
     52  * matrix transforms objects from eye space into clip space.
     53  * </p>
     54  * <p>Another purpose of the camera class is to do frustum culling operations,
     55  * defined by six planes which define a 3D frustum shape, it is possible to
     56  * test if an object bounded by a mathematically defined volume is inside
     57  * the camera frustum, and thus to avoid rendering objects that are outside
     58  * the frustum
     59  * </p>
     60  *
     61  * @author Mark Powell
     62  * @author Joshua Slack
     63  */
     64 public class Camera implements Savable, Cloneable {
     65 
     66     private static final Logger logger = Logger.getLogger(Camera.class.getName());
     67 
     68     /**
     69      * The <code>FrustumIntersect</code> enum is returned as a result
     70      * of a culling check operation,
     71      * see {@link #contains(com.jme3.bounding.BoundingVolume) }
     72      */
     73     public enum FrustumIntersect {
     74 
     75         /**
     76          * defines a constant assigned to spatials that are completely outside
     77          * of this camera's view frustum.
     78          */
     79         Outside,
     80         /**
     81          * defines a constant assigned to spatials that are completely inside
     82          * the camera's view frustum.
     83          */
     84         Inside,
     85         /**
     86          * defines a constant assigned to spatials that are intersecting one of
     87          * the six planes that define the view frustum.
     88          */
     89         Intersects;
     90     }
     91     /**
     92      * LEFT_PLANE represents the left plane of the camera frustum.
     93      */
     94     private static final int LEFT_PLANE = 0;
     95     /**
     96      * RIGHT_PLANE represents the right plane of the camera frustum.
     97      */
     98     private static final int RIGHT_PLANE = 1;
     99     /**
    100      * BOTTOM_PLANE represents the bottom plane of the camera frustum.
    101      */
    102     private static final int BOTTOM_PLANE = 2;
    103     /**
    104      * TOP_PLANE represents the top plane of the camera frustum.
    105      */
    106     private static final int TOP_PLANE = 3;
    107     /**
    108      * FAR_PLANE represents the far plane of the camera frustum.
    109      */
    110     private static final int FAR_PLANE = 4;
    111     /**
    112      * NEAR_PLANE represents the near plane of the camera frustum.
    113      */
    114     private static final int NEAR_PLANE = 5;
    115     /**
    116      * FRUSTUM_PLANES represents the number of planes of the camera frustum.
    117      */
    118     private static final int FRUSTUM_PLANES = 6;
    119     /**
    120      * MAX_WORLD_PLANES holds the maximum planes allowed by the system.
    121      */
    122     private static final int MAX_WORLD_PLANES = 6;
    123     /**
    124      * Camera's location
    125      */
    126     protected Vector3f location;
    127     /**
    128      * The orientation of the camera.
    129      */
    130     protected Quaternion rotation;
    131     /**
    132      * Distance from camera to near frustum plane.
    133      */
    134     protected float frustumNear;
    135     /**
    136      * Distance from camera to far frustum plane.
    137      */
    138     protected float frustumFar;
    139     /**
    140      * Distance from camera to left frustum plane.
    141      */
    142     protected float frustumLeft;
    143     /**
    144      * Distance from camera to right frustum plane.
    145      */
    146     protected float frustumRight;
    147     /**
    148      * Distance from camera to top frustum plane.
    149      */
    150     protected float frustumTop;
    151     /**
    152      * Distance from camera to bottom frustum plane.
    153      */
    154     protected float frustumBottom;
    155     //Temporary values computed in onFrustumChange that are needed if a
    156     //call is made to onFrameChange.
    157     protected float[] coeffLeft;
    158     protected float[] coeffRight;
    159     protected float[] coeffBottom;
    160     protected float[] coeffTop;
    161     //view port coordinates
    162     /**
    163      * Percent value on display where horizontal viewing starts for this camera.
    164      * Default is 0.
    165      */
    166     protected float viewPortLeft;
    167     /**
    168      * Percent value on display where horizontal viewing ends for this camera.
    169      * Default is 1.
    170      */
    171     protected float viewPortRight;
    172     /**
    173      * Percent value on display where vertical viewing ends for this camera.
    174      * Default is 1.
    175      */
    176     protected float viewPortTop;
    177     /**
    178      * Percent value on display where vertical viewing begins for this camera.
    179      * Default is 0.
    180      */
    181     protected float viewPortBottom;
    182     /**
    183      * Array holding the planes that this camera will check for culling.
    184      */
    185     protected Plane[] worldPlane;
    186     /**
    187      * A mask value set during contains() that allows fast culling of a Node's
    188      * children.
    189      */
    190     private int planeState;
    191     protected int width;
    192     protected int height;
    193     protected boolean viewportChanged = true;
    194     /**
    195      * store the value for field parallelProjection
    196      */
    197     private boolean parallelProjection;
    198     protected Matrix4f projectionMatrixOverride;
    199     protected Matrix4f viewMatrix = new Matrix4f();
    200     protected Matrix4f projectionMatrix = new Matrix4f();
    201     protected Matrix4f viewProjectionMatrix = new Matrix4f();
    202     private BoundingBox guiBounding = new BoundingBox();
    203     /** The camera's name. */
    204     protected String name;
    205 
    206     /**
    207      * Serialization only. Do not use.
    208      */
    209     public Camera() {
    210         worldPlane = new Plane[MAX_WORLD_PLANES];
    211         for (int i = 0; i < MAX_WORLD_PLANES; i++) {
    212             worldPlane[i] = new Plane();
    213         }
    214     }
    215 
    216     /**
    217      * Constructor instantiates a new <code>Camera</code> object. All
    218      * values of the camera are set to default.
    219      */
    220     public Camera(int width, int height) {
    221         this();
    222         location = new Vector3f();
    223         rotation = new Quaternion();
    224 
    225         frustumNear = 1.0f;
    226         frustumFar = 2.0f;
    227         frustumLeft = -0.5f;
    228         frustumRight = 0.5f;
    229         frustumTop = 0.5f;
    230         frustumBottom = -0.5f;
    231 
    232         coeffLeft = new float[2];
    233         coeffRight = new float[2];
    234         coeffBottom = new float[2];
    235         coeffTop = new float[2];
    236 
    237         viewPortLeft = 0.0f;
    238         viewPortRight = 1.0f;
    239         viewPortTop = 1.0f;
    240         viewPortBottom = 0.0f;
    241 
    242         this.width = width;
    243         this.height = height;
    244 
    245         onFrustumChange();
    246         onViewPortChange();
    247         onFrameChange();
    248 
    249         logger.log(Level.INFO, "Camera created (W: {0}, H: {1})", new Object[]{width, height});
    250     }
    251 
    252     @Override
    253     public Camera clone() {
    254         try {
    255             Camera cam = (Camera) super.clone();
    256             cam.viewportChanged = true;
    257             cam.planeState = 0;
    258 
    259             cam.worldPlane = new Plane[MAX_WORLD_PLANES];
    260             for (int i = 0; i < worldPlane.length; i++) {
    261                 cam.worldPlane[i] = worldPlane[i].clone();
    262             }
    263 
    264             cam.coeffLeft = new float[2];
    265             cam.coeffRight = new float[2];
    266             cam.coeffBottom = new float[2];
    267             cam.coeffTop = new float[2];
    268 
    269             cam.location = location.clone();
    270             cam.rotation = rotation.clone();
    271 
    272             if (projectionMatrixOverride != null) {
    273                 cam.projectionMatrixOverride = projectionMatrixOverride.clone();
    274             }
    275 
    276             cam.viewMatrix = viewMatrix.clone();
    277             cam.projectionMatrix = projectionMatrix.clone();
    278             cam.viewProjectionMatrix = viewProjectionMatrix.clone();
    279             cam.guiBounding = (BoundingBox) guiBounding.clone();
    280 
    281             cam.update();
    282 
    283             return cam;
    284         } catch (CloneNotSupportedException ex) {
    285             throw new AssertionError();
    286         }
    287     }
    288 
    289 	/**
    290 	 * This method copise the settings of the given camera.
    291 	 *
    292 	 * @param cam
    293 	 *            the camera we copy the settings from
    294 	 */
    295     public void copyFrom(Camera cam) {
    296     	location.set(cam.location);
    297         rotation.set(cam.rotation);
    298 
    299         frustumNear = cam.frustumNear;
    300         frustumFar = cam.frustumFar;
    301         frustumLeft = cam.frustumLeft;
    302         frustumRight = cam.frustumRight;
    303         frustumTop = cam.frustumTop;
    304         frustumBottom = cam.frustumBottom;
    305 
    306         coeffLeft[0] = cam.coeffLeft[0];
    307         coeffLeft[1] = cam.coeffLeft[1];
    308         coeffRight[0] = cam.coeffRight[0];
    309         coeffRight[1] = cam.coeffRight[1];
    310         coeffBottom[0] = cam.coeffBottom[0];
    311         coeffBottom[1] = cam.coeffBottom[1];
    312         coeffTop[0] = cam.coeffTop[0];
    313         coeffTop[1] = cam.coeffTop[1];
    314 
    315         viewPortLeft = cam.viewPortLeft;
    316         viewPortRight = cam.viewPortRight;
    317         viewPortTop = cam.viewPortTop;
    318         viewPortBottom = cam.viewPortBottom;
    319 
    320         this.width = cam.width;
    321         this.height = cam.height;
    322 
    323         this.planeState = cam.planeState;
    324         this.viewportChanged = cam.viewportChanged;
    325         for (int i = 0; i < MAX_WORLD_PLANES; ++i) {
    326             worldPlane[i].setNormal(cam.worldPlane[i].getNormal());
    327             worldPlane[i].setConstant(cam.worldPlane[i].getConstant());
    328         }
    329 
    330         this.parallelProjection = cam.parallelProjection;
    331         if(cam.projectionMatrixOverride != null) {
    332         	if(this.projectionMatrixOverride == null) {
    333         		this.projectionMatrixOverride = cam.projectionMatrixOverride.clone();
    334         	} else {
    335         		this.projectionMatrixOverride.set(cam.projectionMatrixOverride);
    336         	}
    337         } else {
    338         	this.projectionMatrixOverride = null;
    339         }
    340         this.viewMatrix.set(cam.viewMatrix);
    341         this.projectionMatrix.set(cam.projectionMatrix);
    342         this.viewProjectionMatrix.set(cam.viewProjectionMatrix);
    343 
    344         this.guiBounding.setXExtent(cam.guiBounding.getXExtent());
    345         this.guiBounding.setYExtent(cam.guiBounding.getYExtent());
    346         this.guiBounding.setZExtent(cam.guiBounding.getZExtent());
    347         this.guiBounding.setCenter(cam.guiBounding.getCenter());
    348         this.guiBounding.setCheckPlane(cam.guiBounding.getCheckPlane());
    349 
    350         this.name = cam.name;
    351     }
    352 
    353     /**
    354      * This method sets the cameras name.
    355      * @param name the cameras name
    356      */
    357     public void setName(String name) {
    358         this.name = name;
    359     }
    360 
    361     /**
    362      * This method returns the cameras name.
    363      * @return the cameras name
    364      */
    365     public String getName() {
    366         return name;
    367     }
    368 
    369     /**
    370      * Sets a clipPlane for this camera.
    371      * The cliPlane is used to recompute the projectionMatrix using the plane as the near plane
    372      * This technique is known as the oblique near-plane clipping method introduced by Eric Lengyel
    373      * more info here
    374      * <ul>
    375      * <li><a href="http://www.terathon.com/code/oblique.html">http://www.terathon.com/code/oblique.html</a>
    376      * <li><a href="http://aras-p.info/texts/obliqueortho.html">http://aras-p.info/texts/obliqueortho.html</a>
    377      * <li><a href="http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html">http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html</a>
    378      * </ul>
    379      *
    380      * Note that this will work properly only if it's called on each update, and be aware that it won't work properly with the sky bucket.
    381      * if you want to handle the sky bucket, look at how it's done in SimpleWaterProcessor.java
    382      * @param clipPlane the plane
    383      * @param side the side the camera stands from the plane
    384      */
    385     public void setClipPlane(Plane clipPlane, Plane.Side side) {
    386         float sideFactor = 1;
    387         if (side == Plane.Side.Negative) {
    388             sideFactor = -1;
    389         }
    390         //we are on the other side of the plane no need to clip anymore.
    391         if (clipPlane.whichSide(location) == side) {
    392             return;
    393         }
    394         Matrix4f p = projectionMatrix.clone();
    395 
    396         Matrix4f ivm = viewMatrix.clone();
    397 
    398         Vector3f point = clipPlane.getNormal().mult(clipPlane.getConstant());
    399         Vector3f pp = ivm.mult(point);
    400         Vector3f pn = ivm.multNormal(clipPlane.getNormal(), null);
    401         Vector4f clipPlaneV = new Vector4f(pn.x * sideFactor, pn.y * sideFactor, pn.z * sideFactor, -(pp.dot(pn)) * sideFactor);
    402 
    403         Vector4f v = new Vector4f(0, 0, 0, 0);
    404 
    405         v.x = (Math.signum(clipPlaneV.x) + p.m02) / p.m00;
    406         v.y = (Math.signum(clipPlaneV.y) + p.m12) / p.m11;
    407         v.z = -1.0f;
    408         v.w = (1.0f + p.m22) / p.m23;
    409 
    410         float dot = clipPlaneV.dot(v);//clipPlaneV.x * v.x + clipPlaneV.y * v.y + clipPlaneV.z * v.z + clipPlaneV.w * v.w;
    411         Vector4f c = clipPlaneV.mult(2.0f / dot);
    412 
    413         p.m20 = c.x - p.m30;
    414         p.m21 = c.y - p.m31;
    415         p.m22 = c.z - p.m32;
    416         p.m23 = c.w - p.m33;
    417         setProjectionMatrix(p);
    418     }
    419 
    420     /**
    421      * Sets a clipPlane for this camera.
    422      * The cliPlane is used to recompute the projectionMatrix using the plane as the near plane
    423      * This technique is known as the oblique near-plane clipping method introduced by Eric Lengyel
    424      * more info here
    425      * <ul>
    426      * <li><a href="http://www.terathon.com/code/oblique.html">http://www.terathon.com/code/oblique.html</a></li>
    427      * <li><a href="http://aras-p.info/texts/obliqueortho.html">http://aras-p.info/texts/obliqueortho.html</a></li>
    428      * <li><a href="http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html">
    429      * http://hacksoflife.blogspot.com/2008/12/every-now-and-then-i-come-across.html</a></li>
    430      * </ul>
    431      *
    432      * Note that this will work properly only if it's called on each update, and be aware that it won't work properly with the sky bucket.
    433      * if you want to handle the sky bucket, look at how it's done in SimpleWaterProcessor.java
    434      * @param clipPlane the plane
    435      */
    436     public void setClipPlane(Plane clipPlane) {
    437         setClipPlane(clipPlane, clipPlane.whichSide(location));
    438     }
    439 
    440     /**
    441      * Resizes this camera's view with the given width and height. This is
    442      * similar to constructing a new camera, but reusing the same Object. This
    443      * method is called by an associated {@link RenderManager} to notify the camera of
    444      * changes in the display dimensions.
    445      *
    446      * @param width the view width
    447      * @param height the view height
    448      * @param fixAspect If true, the camera's aspect ratio will be recomputed.
    449      * Recomputing the aspect ratio requires changing the frustum values.
    450      */
    451     public void resize(int width, int height, boolean fixAspect) {
    452         this.width = width;
    453         this.height = height;
    454         onViewPortChange();
    455 
    456         if (fixAspect /*&& !parallelProjection*/) {
    457             frustumRight = frustumTop * ((float) width / height);
    458             frustumLeft = -frustumRight;
    459             onFrustumChange();
    460         }
    461     }
    462 
    463     /**
    464      * <code>getFrustumBottom</code> returns the value of the bottom frustum
    465      * plane.
    466      *
    467      * @return the value of the bottom frustum plane.
    468      */
    469     public float getFrustumBottom() {
    470         return frustumBottom;
    471     }
    472 
    473     /**
    474      * <code>setFrustumBottom</code> sets the value of the bottom frustum
    475      * plane.
    476      *
    477      * @param frustumBottom the value of the bottom frustum plane.
    478      */
    479     public void setFrustumBottom(float frustumBottom) {
    480         this.frustumBottom = frustumBottom;
    481         onFrustumChange();
    482     }
    483 
    484     /**
    485      * <code>getFrustumFar</code> gets the value of the far frustum plane.
    486      *
    487      * @return the value of the far frustum plane.
    488      */
    489     public float getFrustumFar() {
    490         return frustumFar;
    491     }
    492 
    493     /**
    494      * <code>setFrustumFar</code> sets the value of the far frustum plane.
    495      *
    496      * @param frustumFar the value of the far frustum plane.
    497      */
    498     public void setFrustumFar(float frustumFar) {
    499         this.frustumFar = frustumFar;
    500         onFrustumChange();
    501     }
    502 
    503     /**
    504      * <code>getFrustumLeft</code> gets the value of the left frustum plane.
    505      *
    506      * @return the value of the left frustum plane.
    507      */
    508     public float getFrustumLeft() {
    509         return frustumLeft;
    510     }
    511 
    512     /**
    513      * <code>setFrustumLeft</code> sets the value of the left frustum plane.
    514      *
    515      * @param frustumLeft the value of the left frustum plane.
    516      */
    517     public void setFrustumLeft(float frustumLeft) {
    518         this.frustumLeft = frustumLeft;
    519         onFrustumChange();
    520     }
    521 
    522     /**
    523      * <code>getFrustumNear</code> gets the value of the near frustum plane.
    524      *
    525      * @return the value of the near frustum plane.
    526      */
    527     public float getFrustumNear() {
    528         return frustumNear;
    529     }
    530 
    531     /**
    532      * <code>setFrustumNear</code> sets the value of the near frustum plane.
    533      *
    534      * @param frustumNear the value of the near frustum plane.
    535      */
    536     public void setFrustumNear(float frustumNear) {
    537         this.frustumNear = frustumNear;
    538         onFrustumChange();
    539     }
    540 
    541     /**
    542      * <code>getFrustumRight</code> gets the value of the right frustum plane.
    543      *
    544      * @return frustumRight the value of the right frustum plane.
    545      */
    546     public float getFrustumRight() {
    547         return frustumRight;
    548     }
    549 
    550     /**
    551      * <code>setFrustumRight</code> sets the value of the right frustum plane.
    552      *
    553      * @param frustumRight the value of the right frustum plane.
    554      */
    555     public void setFrustumRight(float frustumRight) {
    556         this.frustumRight = frustumRight;
    557         onFrustumChange();
    558     }
    559 
    560     /**
    561      * <code>getFrustumTop</code> gets the value of the top frustum plane.
    562      *
    563      * @return the value of the top frustum plane.
    564      */
    565     public float getFrustumTop() {
    566         return frustumTop;
    567     }
    568 
    569     /**
    570      * <code>setFrustumTop</code> sets the value of the top frustum plane.
    571      *
    572      * @param frustumTop the value of the top frustum plane.
    573      */
    574     public void setFrustumTop(float frustumTop) {
    575         this.frustumTop = frustumTop;
    576         onFrustumChange();
    577     }
    578 
    579     /**
    580      * <code>getLocation</code> retrieves the location vector of the camera.
    581      *
    582      * @return the position of the camera.
    583      * @see Camera#getLocation()
    584      */
    585     public Vector3f getLocation() {
    586         return location;
    587     }
    588 
    589     /**
    590      * <code>getRotation</code> retrieves the rotation quaternion of the camera.
    591      *
    592      * @return the rotation of the camera.
    593      */
    594     public Quaternion getRotation() {
    595         return rotation;
    596     }
    597 
    598     /**
    599      * <code>getDirection</code> retrieves the direction vector the camera is
    600      * facing.
    601      *
    602      * @return the direction the camera is facing.
    603      * @see Camera#getDirection()
    604      */
    605     public Vector3f getDirection() {
    606         return rotation.getRotationColumn(2);
    607     }
    608 
    609     /**
    610      * <code>getLeft</code> retrieves the left axis of the camera.
    611      *
    612      * @return the left axis of the camera.
    613      * @see Camera#getLeft()
    614      */
    615     public Vector3f getLeft() {
    616         return rotation.getRotationColumn(0);
    617     }
    618 
    619     /**
    620      * <code>getUp</code> retrieves the up axis of the camera.
    621      *
    622      * @return the up axis of the camera.
    623      * @see Camera#getUp()
    624      */
    625     public Vector3f getUp() {
    626         return rotation.getRotationColumn(1);
    627     }
    628 
    629     /**
    630      * <code>getDirection</code> retrieves the direction vector the camera is
    631      * facing.
    632      *
    633      * @return the direction the camera is facing.
    634      * @see Camera#getDirection()
    635      */
    636     public Vector3f getDirection(Vector3f store) {
    637         return rotation.getRotationColumn(2, store);
    638     }
    639 
    640     /**
    641      * <code>getLeft</code> retrieves the left axis of the camera.
    642      *
    643      * @return the left axis of the camera.
    644      * @see Camera#getLeft()
    645      */
    646     public Vector3f getLeft(Vector3f store) {
    647         return rotation.getRotationColumn(0, store);
    648     }
    649 
    650     /**
    651      * <code>getUp</code> retrieves the up axis of the camera.
    652      *
    653      * @return the up axis of the camera.
    654      * @see Camera#getUp()
    655      */
    656     public Vector3f getUp(Vector3f store) {
    657         return rotation.getRotationColumn(1, store);
    658     }
    659 
    660     /**
    661      * <code>setLocation</code> sets the position of the camera.
    662      *
    663      * @param location the position of the camera.
    664      */
    665     public void setLocation(Vector3f location) {
    666         this.location.set(location);
    667         onFrameChange();
    668     }
    669 
    670     /**
    671      * <code>setRotation</code> sets the orientation of this camera.
    672      * This will be equivelant to setting each of the axes:
    673      * <code><br>
    674      * cam.setLeft(rotation.getRotationColumn(0));<br>
    675      * cam.setUp(rotation.getRotationColumn(1));<br>
    676      * cam.setDirection(rotation.getRotationColumn(2));<br>
    677      * </code>
    678      *
    679      * @param rotation the rotation of this camera
    680      */
    681     public void setRotation(Quaternion rotation) {
    682         this.rotation.set(rotation);
    683         onFrameChange();
    684     }
    685 
    686     /**
    687      * <code>lookAtDirection</code> sets the direction the camera is facing
    688      * given a direction and an up vector.
    689      *
    690      * @param direction the direction this camera is facing.
    691      */
    692     public void lookAtDirection(Vector3f direction, Vector3f up) {
    693         this.rotation.lookAt(direction, up);
    694         onFrameChange();
    695     }
    696 
    697     /**
    698      * <code>setAxes</code> sets the axes (left, up and direction) for this
    699      * camera.
    700      *
    701      * @param left      the left axis of the camera.
    702      * @param up        the up axis of the camera.
    703      * @param direction the direction the camera is facing.
    704      *
    705      * @see Camera#setAxes(com.jme3.math.Quaternion)
    706      */
    707     public void setAxes(Vector3f left, Vector3f up, Vector3f direction) {
    708         this.rotation.fromAxes(left, up, direction);
    709         onFrameChange();
    710     }
    711 
    712     /**
    713      * <code>setAxes</code> uses a rotational matrix to set the axes of the
    714      * camera.
    715      *
    716      * @param axes the matrix that defines the orientation of the camera.
    717      */
    718     public void setAxes(Quaternion axes) {
    719         this.rotation.set(axes);
    720         onFrameChange();
    721     }
    722 
    723     /**
    724      * normalize normalizes the camera vectors.
    725      */
    726     public void normalize() {
    727         this.rotation.normalizeLocal();
    728         onFrameChange();
    729     }
    730 
    731     /**
    732      * <code>setFrustum</code> sets the frustum of this camera object.
    733      *
    734      * @param near   the near plane.
    735      * @param far    the far plane.
    736      * @param left   the left plane.
    737      * @param right  the right plane.
    738      * @param top    the top plane.
    739      * @param bottom the bottom plane.
    740      * @see Camera#setFrustum(float, float, float, float,
    741      *      float, float)
    742      */
    743     public void setFrustum(float near, float far, float left, float right,
    744             float top, float bottom) {
    745 
    746         frustumNear = near;
    747         frustumFar = far;
    748         frustumLeft = left;
    749         frustumRight = right;
    750         frustumTop = top;
    751         frustumBottom = bottom;
    752         onFrustumChange();
    753     }
    754 
    755     /**
    756      * <code>setFrustumPerspective</code> defines the frustum for the camera.  This
    757      * frustum is defined by a viewing angle, aspect ratio, and near/far planes
    758      *
    759      * @param fovY   Frame of view angle along the Y in degrees.
    760      * @param aspect Width:Height ratio
    761      * @param near   Near view plane distance
    762      * @param far    Far view plane distance
    763      */
    764     public void setFrustumPerspective(float fovY, float aspect, float near,
    765             float far) {
    766         if (Float.isNaN(aspect) || Float.isInfinite(aspect)) {
    767             // ignore.
    768             logger.log(Level.WARNING, "Invalid aspect given to setFrustumPerspective: {0}", aspect);
    769             return;
    770         }
    771 
    772         float h = FastMath.tan(fovY * FastMath.DEG_TO_RAD * .5f) * near;
    773         float w = h * aspect;
    774         frustumLeft = -w;
    775         frustumRight = w;
    776         frustumBottom = -h;
    777         frustumTop = h;
    778         frustumNear = near;
    779         frustumFar = far;
    780 
    781         onFrustumChange();
    782     }
    783 
    784     /**
    785      * <code>setFrame</code> sets the orientation and location of the camera.
    786      *
    787      * @param location  the point position of the camera.
    788      * @param left      the left axis of the camera.
    789      * @param up        the up axis of the camera.
    790      * @param direction the facing of the camera.
    791      * @see Camera#setFrame(com.jme3.math.Vector3f,
    792      *      com.jme3.math.Vector3f, com.jme3.math.Vector3f, com.jme3.math.Vector3f)
    793      */
    794     public void setFrame(Vector3f location, Vector3f left, Vector3f up,
    795             Vector3f direction) {
    796 
    797         this.location = location;
    798         this.rotation.fromAxes(left, up, direction);
    799         onFrameChange();
    800     }
    801 
    802     /**
    803      * <code>lookAt</code> is a convienence method for auto-setting the frame
    804      * based on a world position the user desires the camera to look at. It
    805      * repoints the camera towards the given position using the difference
    806      * between the position and the current camera location as a direction
    807      * vector and the worldUpVector to compute up and left camera vectors.
    808      *
    809      * @param pos           where to look at in terms of world coordinates
    810      * @param worldUpVector a normalized vector indicating the up direction of the world.
    811      *                      (typically {0, 1, 0} in jME.)
    812      */
    813     public void lookAt(Vector3f pos, Vector3f worldUpVector) {
    814         TempVars vars = TempVars.get();
    815         Vector3f newDirection = vars.vect1;
    816         Vector3f newUp = vars.vect2;
    817         Vector3f newLeft = vars.vect3;
    818 
    819         newDirection.set(pos).subtractLocal(location).normalizeLocal();
    820 
    821         newUp.set(worldUpVector).normalizeLocal();
    822         if (newUp.equals(Vector3f.ZERO)) {
    823             newUp.set(Vector3f.UNIT_Y);
    824         }
    825 
    826         newLeft.set(newUp).crossLocal(newDirection).normalizeLocal();
    827         if (newLeft.equals(Vector3f.ZERO)) {
    828             if (newDirection.x != 0) {
    829                 newLeft.set(newDirection.y, -newDirection.x, 0f);
    830             } else {
    831                 newLeft.set(0f, newDirection.z, -newDirection.y);
    832             }
    833         }
    834 
    835         newUp.set(newDirection).crossLocal(newLeft).normalizeLocal();
    836 
    837         this.rotation.fromAxes(newLeft, newUp, newDirection);
    838         this.rotation.normalizeLocal();
    839         vars.release();
    840 
    841         onFrameChange();
    842     }
    843 
    844     /**
    845      * <code>setFrame</code> sets the orientation and location of the camera.
    846      *
    847      * @param location
    848      *            the point position of the camera.
    849      * @param axes
    850      *            the orientation of the camera.
    851      */
    852     public void setFrame(Vector3f location, Quaternion axes) {
    853         this.location = location;
    854         this.rotation.set(axes);
    855         onFrameChange();
    856     }
    857 
    858     /**
    859      * <code>update</code> updates the camera parameters by calling
    860      * <code>onFrustumChange</code>,<code>onViewPortChange</code> and
    861      * <code>onFrameChange</code>.
    862      *
    863      * @see Camera#update()
    864      */
    865     public void update() {
    866         onFrustumChange();
    867         onViewPortChange();
    868         onFrameChange();
    869     }
    870 
    871     /**
    872      * <code>getPlaneState</code> returns the state of the frustum planes. So
    873      * checks can be made as to which frustum plane has been examined for
    874      * culling thus far.
    875      *
    876      * @return the current plane state int.
    877      */
    878     public int getPlaneState() {
    879         return planeState;
    880     }
    881 
    882     /**
    883      * <code>setPlaneState</code> sets the state to keep track of tested
    884      * planes for culling.
    885      *
    886      * @param planeState the updated state.
    887      */
    888     public void setPlaneState(int planeState) {
    889         this.planeState = planeState;
    890     }
    891 
    892     /**
    893      * <code>getViewPortLeft</code> gets the left boundary of the viewport
    894      *
    895      * @return the left boundary of the viewport
    896      */
    897     public float getViewPortLeft() {
    898         return viewPortLeft;
    899     }
    900 
    901     /**
    902      * <code>setViewPortLeft</code> sets the left boundary of the viewport
    903      *
    904      * @param left the left boundary of the viewport
    905      */
    906     public void setViewPortLeft(float left) {
    907         viewPortLeft = left;
    908         onViewPortChange();
    909     }
    910 
    911     /**
    912      * <code>getViewPortRight</code> gets the right boundary of the viewport
    913      *
    914      * @return the right boundary of the viewport
    915      */
    916     public float getViewPortRight() {
    917         return viewPortRight;
    918     }
    919 
    920     /**
    921      * <code>setViewPortRight</code> sets the right boundary of the viewport
    922      *
    923      * @param right the right boundary of the viewport
    924      */
    925     public void setViewPortRight(float right) {
    926         viewPortRight = right;
    927         onViewPortChange();
    928     }
    929 
    930     /**
    931      * <code>getViewPortTop</code> gets the top boundary of the viewport
    932      *
    933      * @return the top boundary of the viewport
    934      */
    935     public float getViewPortTop() {
    936         return viewPortTop;
    937     }
    938 
    939     /**
    940      * <code>setViewPortTop</code> sets the top boundary of the viewport
    941      *
    942      * @param top the top boundary of the viewport
    943      */
    944     public void setViewPortTop(float top) {
    945         viewPortTop = top;
    946         onViewPortChange();
    947     }
    948 
    949     /**
    950      * <code>getViewPortBottom</code> gets the bottom boundary of the viewport
    951      *
    952      * @return the bottom boundary of the viewport
    953      */
    954     public float getViewPortBottom() {
    955         return viewPortBottom;
    956     }
    957 
    958     /**
    959      * <code>setViewPortBottom</code> sets the bottom boundary of the viewport
    960      *
    961      * @param bottom the bottom boundary of the viewport
    962      */
    963     public void setViewPortBottom(float bottom) {
    964         viewPortBottom = bottom;
    965         onViewPortChange();
    966     }
    967 
    968     /**
    969      * <code>setViewPort</code> sets the boundaries of the viewport
    970      *
    971      * @param left   the left boundary of the viewport (default: 0)
    972      * @param right  the right boundary of the viewport (default: 1)
    973      * @param bottom the bottom boundary of the viewport (default: 0)
    974      * @param top    the top boundary of the viewport (default: 1)
    975      */
    976     public void setViewPort(float left, float right, float bottom, float top) {
    977         this.viewPortLeft = left;
    978         this.viewPortRight = right;
    979         this.viewPortBottom = bottom;
    980         this.viewPortTop = top;
    981         onViewPortChange();
    982     }
    983 
    984     /**
    985      * Returns the pseudo distance from the given position to the near
    986      * plane of the camera. This is used for render queue sorting.
    987      * @param pos The position to compute a distance to.
    988      * @return Distance from the far plane to the point.
    989      */
    990     public float distanceToNearPlane(Vector3f pos) {
    991         return worldPlane[NEAR_PLANE].pseudoDistance(pos);
    992     }
    993 
    994     /**
    995      * <code>contains</code> tests a bounding volume against the planes of the
    996      * camera's frustum. The frustums planes are set such that the normals all
    997      * face in towards the viewable scene. Therefore, if the bounding volume is
    998      * on the negative side of the plane is can be culled out.
    999      *
   1000      * NOTE: This method is used internally for culling, for public usage,
   1001      * the plane state of the bounding volume must be saved and restored, e.g:
   1002      * <code>BoundingVolume bv;<br/>
   1003      * Camera c;<br/>
   1004      * int planeState = bv.getPlaneState();<br/>
   1005      * bv.setPlaneState(0);<br/>
   1006      * c.contains(bv);<br/>
   1007      * bv.setPlaneState(plateState);<br/>
   1008      * </code>
   1009      *
   1010      * @param bound the bound to check for culling
   1011      * @return See enums in <code>FrustumIntersect</code>
   1012      */
   1013     public FrustumIntersect contains(BoundingVolume bound) {
   1014         if (bound == null) {
   1015             return FrustumIntersect.Inside;
   1016         }
   1017 
   1018         int mask;
   1019         FrustumIntersect rVal = FrustumIntersect.Inside;
   1020 
   1021         for (int planeCounter = FRUSTUM_PLANES; planeCounter >= 0; planeCounter--) {
   1022             if (planeCounter == bound.getCheckPlane()) {
   1023                 continue; // we have already checked this plane at first iteration
   1024             }
   1025             int planeId = (planeCounter == FRUSTUM_PLANES) ? bound.getCheckPlane() : planeCounter;
   1026 //            int planeId = planeCounter;
   1027 
   1028             mask = 1 << (planeId);
   1029             if ((planeState & mask) == 0) {
   1030                 Plane.Side side = bound.whichSide(worldPlane[planeId]);
   1031 
   1032                 if (side == Plane.Side.Negative) {
   1033                     //object is outside of frustum
   1034                     bound.setCheckPlane(planeId);
   1035                     return FrustumIntersect.Outside;
   1036                 } else if (side == Plane.Side.Positive) {
   1037                     //object is visible on *this* plane, so mark this plane
   1038                     //so that we don't check it for sub nodes.
   1039                     planeState |= mask;
   1040                 } else {
   1041                     rVal = FrustumIntersect.Intersects;
   1042                 }
   1043             }
   1044         }
   1045 
   1046         return rVal;
   1047     }
   1048 
   1049     /**
   1050      * <code>containsGui</code> tests a bounding volume against the ortho
   1051      * bounding box of the camera. A bounding box spanning from
   1052      * 0, 0 to Width, Height. Constrained by the viewport settings on the
   1053      * camera.
   1054      *
   1055      * @param bound the bound to check for culling
   1056      * @return True if the camera contains the gui element bounding volume.
   1057      */
   1058     public boolean containsGui(BoundingVolume bound) {
   1059         return guiBounding.intersects(bound);
   1060     }
   1061 
   1062     /**
   1063      * @return the view matrix of the camera.
   1064      * The view matrix transforms world space into eye space.
   1065      * This matrix is usually defined by the position and
   1066      * orientation of the camera.
   1067      */
   1068     public Matrix4f getViewMatrix() {
   1069         return viewMatrix;
   1070     }
   1071 
   1072     /**
   1073      * Overrides the projection matrix used by the camera. Will
   1074      * use the matrix for computing the view projection matrix as well.
   1075      * Use null argument to return to normal functionality.
   1076      *
   1077      * @param projMatrix
   1078      */
   1079     public void setProjectionMatrix(Matrix4f projMatrix) {
   1080         projectionMatrixOverride = projMatrix;
   1081         updateViewProjection();
   1082     }
   1083 
   1084     /**
   1085      * @return the projection matrix of the camera.
   1086      * The view projection matrix  transforms eye space into clip space.
   1087      * This matrix is usually defined by the viewport and perspective settings
   1088      * of the camera.
   1089      */
   1090     public Matrix4f getProjectionMatrix() {
   1091         if (projectionMatrixOverride != null) {
   1092             return projectionMatrixOverride;
   1093         }
   1094 
   1095         return projectionMatrix;
   1096     }
   1097 
   1098     /**
   1099      * Updates the view projection matrix.
   1100      */
   1101     public void updateViewProjection() {
   1102         if (projectionMatrixOverride != null) {
   1103             viewProjectionMatrix.set(projectionMatrixOverride).multLocal(viewMatrix);
   1104         } else {
   1105             //viewProjectionMatrix.set(viewMatrix).multLocal(projectionMatrix);
   1106             viewProjectionMatrix.set(projectionMatrix).multLocal(viewMatrix);
   1107         }
   1108     }
   1109 
   1110     /**
   1111      * @return The result of multiplying the projection matrix by the view
   1112      * matrix. This matrix is required for rendering an object. It is
   1113      * precomputed so as to not compute it every time an object is rendered.
   1114      */
   1115     public Matrix4f getViewProjectionMatrix() {
   1116         return viewProjectionMatrix;
   1117     }
   1118 
   1119     /**
   1120      * @return True if the viewport (width, height, left, right, bottom, up)
   1121      * has been changed. This is needed in the renderer so that the proper
   1122      * viewport can be set-up.
   1123      */
   1124     public boolean isViewportChanged() {
   1125         return viewportChanged;
   1126     }
   1127 
   1128     /**
   1129      * Clears the viewport changed flag once it has been updated inside
   1130      * the renderer.
   1131      */
   1132     public void clearViewportChanged() {
   1133         viewportChanged = false;
   1134     }
   1135 
   1136     /**
   1137      * Called when the viewport has been changed.
   1138      */
   1139     public void onViewPortChange() {
   1140         viewportChanged = true;
   1141         setGuiBounding();
   1142     }
   1143 
   1144     private void setGuiBounding() {
   1145         float sx = width * viewPortLeft;
   1146         float ex = width * viewPortRight;
   1147         float sy = height * viewPortBottom;
   1148         float ey = height * viewPortTop;
   1149         float xExtent = Math.max(0f, (ex - sx) / 2f);
   1150         float yExtent = Math.max(0f, (ey - sy) / 2f);
   1151         guiBounding.setCenter(new Vector3f(sx + xExtent, sy + yExtent, 0));
   1152         guiBounding.setXExtent(xExtent);
   1153         guiBounding.setYExtent(yExtent);
   1154         guiBounding.setZExtent(Float.MAX_VALUE);
   1155     }
   1156 
   1157     /**
   1158      * <code>onFrustumChange</code> updates the frustum to reflect any changes
   1159      * made to the planes. The new frustum values are kept in a temporary
   1160      * location for use when calculating the new frame. The projection
   1161      * matrix is updated to reflect the current values of the frustum.
   1162      */
   1163     public void onFrustumChange() {
   1164         if (!isParallelProjection()) {
   1165             float nearSquared = frustumNear * frustumNear;
   1166             float leftSquared = frustumLeft * frustumLeft;
   1167             float rightSquared = frustumRight * frustumRight;
   1168             float bottomSquared = frustumBottom * frustumBottom;
   1169             float topSquared = frustumTop * frustumTop;
   1170 
   1171             float inverseLength = FastMath.invSqrt(nearSquared + leftSquared);
   1172             coeffLeft[0] = frustumNear * inverseLength;
   1173             coeffLeft[1] = -frustumLeft * inverseLength;
   1174 
   1175             inverseLength = FastMath.invSqrt(nearSquared + rightSquared);
   1176             coeffRight[0] = -frustumNear * inverseLength;
   1177             coeffRight[1] = frustumRight * inverseLength;
   1178 
   1179             inverseLength = FastMath.invSqrt(nearSquared + bottomSquared);
   1180             coeffBottom[0] = frustumNear * inverseLength;
   1181             coeffBottom[1] = -frustumBottom * inverseLength;
   1182 
   1183             inverseLength = FastMath.invSqrt(nearSquared + topSquared);
   1184             coeffTop[0] = -frustumNear * inverseLength;
   1185             coeffTop[1] = frustumTop * inverseLength;
   1186         } else {
   1187             coeffLeft[0] = 1;
   1188             coeffLeft[1] = 0;
   1189 
   1190             coeffRight[0] = -1;
   1191             coeffRight[1] = 0;
   1192 
   1193             coeffBottom[0] = 1;
   1194             coeffBottom[1] = 0;
   1195 
   1196             coeffTop[0] = -1;
   1197             coeffTop[1] = 0;
   1198         }
   1199 
   1200         projectionMatrix.fromFrustum(frustumNear, frustumFar, frustumLeft, frustumRight, frustumTop, frustumBottom, parallelProjection);
   1201 //        projectionMatrix.transposeLocal();
   1202 
   1203         // The frame is effected by the frustum values
   1204         // update it as well
   1205         onFrameChange();
   1206     }
   1207 
   1208     /**
   1209      * <code>onFrameChange</code> updates the view frame of the camera.
   1210      */
   1211     public void onFrameChange() {
   1212         TempVars vars = TempVars.get();
   1213 
   1214         Vector3f left = getLeft(vars.vect1);
   1215         Vector3f direction = getDirection(vars.vect2);
   1216         Vector3f up = getUp(vars.vect3);
   1217 
   1218         float dirDotLocation = direction.dot(location);
   1219 
   1220         // left plane
   1221         Vector3f leftPlaneNormal = worldPlane[LEFT_PLANE].getNormal();
   1222         leftPlaneNormal.x = left.x * coeffLeft[0];
   1223         leftPlaneNormal.y = left.y * coeffLeft[0];
   1224         leftPlaneNormal.z = left.z * coeffLeft[0];
   1225         leftPlaneNormal.addLocal(direction.x * coeffLeft[1], direction.y
   1226                 * coeffLeft[1], direction.z * coeffLeft[1]);
   1227         worldPlane[LEFT_PLANE].setConstant(location.dot(leftPlaneNormal));
   1228 
   1229         // right plane
   1230         Vector3f rightPlaneNormal = worldPlane[RIGHT_PLANE].getNormal();
   1231         rightPlaneNormal.x = left.x * coeffRight[0];
   1232         rightPlaneNormal.y = left.y * coeffRight[0];
   1233         rightPlaneNormal.z = left.z * coeffRight[0];
   1234         rightPlaneNormal.addLocal(direction.x * coeffRight[1], direction.y
   1235                 * coeffRight[1], direction.z * coeffRight[1]);
   1236         worldPlane[RIGHT_PLANE].setConstant(location.dot(rightPlaneNormal));
   1237 
   1238         // bottom plane
   1239         Vector3f bottomPlaneNormal = worldPlane[BOTTOM_PLANE].getNormal();
   1240         bottomPlaneNormal.x = up.x * coeffBottom[0];
   1241         bottomPlaneNormal.y = up.y * coeffBottom[0];
   1242         bottomPlaneNormal.z = up.z * coeffBottom[0];
   1243         bottomPlaneNormal.addLocal(direction.x * coeffBottom[1], direction.y
   1244                 * coeffBottom[1], direction.z * coeffBottom[1]);
   1245         worldPlane[BOTTOM_PLANE].setConstant(location.dot(bottomPlaneNormal));
   1246 
   1247         // top plane
   1248         Vector3f topPlaneNormal = worldPlane[TOP_PLANE].getNormal();
   1249         topPlaneNormal.x = up.x * coeffTop[0];
   1250         topPlaneNormal.y = up.y * coeffTop[0];
   1251         topPlaneNormal.z = up.z * coeffTop[0];
   1252         topPlaneNormal.addLocal(direction.x * coeffTop[1], direction.y
   1253                 * coeffTop[1], direction.z * coeffTop[1]);
   1254         worldPlane[TOP_PLANE].setConstant(location.dot(topPlaneNormal));
   1255 
   1256         if (isParallelProjection()) {
   1257             worldPlane[LEFT_PLANE].setConstant(worldPlane[LEFT_PLANE].getConstant() + frustumLeft);
   1258             worldPlane[RIGHT_PLANE].setConstant(worldPlane[RIGHT_PLANE].getConstant() - frustumRight);
   1259             worldPlane[TOP_PLANE].setConstant(worldPlane[TOP_PLANE].getConstant() - frustumTop);
   1260             worldPlane[BOTTOM_PLANE].setConstant(worldPlane[BOTTOM_PLANE].getConstant() + frustumBottom);
   1261         }
   1262 
   1263         // far plane
   1264         worldPlane[FAR_PLANE].setNormal(left);
   1265         worldPlane[FAR_PLANE].setNormal(-direction.x, -direction.y, -direction.z);
   1266         worldPlane[FAR_PLANE].setConstant(-(dirDotLocation + frustumFar));
   1267 
   1268         // near plane
   1269         worldPlane[NEAR_PLANE].setNormal(direction.x, direction.y, direction.z);
   1270         worldPlane[NEAR_PLANE].setConstant(dirDotLocation + frustumNear);
   1271 
   1272         viewMatrix.fromFrame(location, direction, up, left);
   1273 
   1274         vars.release();
   1275 
   1276 //        viewMatrix.transposeLocal();
   1277         updateViewProjection();
   1278     }
   1279 
   1280     /**
   1281      * @return true if parallel projection is enable, false if in normal perspective mode
   1282      * @see #setParallelProjection(boolean)
   1283      */
   1284     public boolean isParallelProjection() {
   1285         return this.parallelProjection;
   1286     }
   1287 
   1288     /**
   1289      * Enable/disable parallel projection.
   1290      *
   1291      * @param value true to set up this camera for parallel projection is enable, false to enter normal perspective mode
   1292      */
   1293     public void setParallelProjection(final boolean value) {
   1294         this.parallelProjection = value;
   1295         onFrustumChange();
   1296     }
   1297 
   1298     /**
   1299      * @see Camera#getWorldCoordinates
   1300      */
   1301     public Vector3f getWorldCoordinates(Vector2f screenPos, float zPos) {
   1302         return getWorldCoordinates(screenPos, zPos, null);
   1303     }
   1304 
   1305     /**
   1306      * @see Camera#getWorldCoordinates
   1307      */
   1308     public Vector3f getWorldCoordinates(Vector2f screenPosition,
   1309             float zPos, Vector3f store) {
   1310         if (store == null) {
   1311             store = new Vector3f();
   1312         }
   1313 
   1314         Matrix4f inverseMat = new Matrix4f(viewProjectionMatrix);
   1315         inverseMat.invertLocal();
   1316 
   1317         store.set(
   1318                 (screenPosition.x / getWidth() - viewPortLeft) / (viewPortRight - viewPortLeft) * 2 - 1,
   1319                 (screenPosition.y / getHeight() - viewPortBottom) / (viewPortTop - viewPortBottom) * 2 - 1,
   1320                 zPos * 2 - 1);
   1321 
   1322         float w = inverseMat.multProj(store, store);
   1323         store.multLocal(1f / w);
   1324 
   1325         return store;
   1326     }
   1327 
   1328     /**
   1329      * Converts the given position from world space to screen space.
   1330      *
   1331      * @see Camera#getScreenCoordinates
   1332      */
   1333     public Vector3f getScreenCoordinates(Vector3f worldPos) {
   1334         return getScreenCoordinates(worldPos, null);
   1335     }
   1336 
   1337     /**
   1338      * Converts the given position from world space to screen space.
   1339      *
   1340      * @see Camera#getScreenCoordinates(Vector3f, Vector3f)
   1341      */
   1342     public Vector3f getScreenCoordinates(Vector3f worldPosition, Vector3f store) {
   1343         if (store == null) {
   1344             store = new Vector3f();
   1345         }
   1346 
   1347 //        TempVars vars = vars.lock();
   1348 //        Quaternion tmp_quat = vars.quat1;
   1349 //        tmp_quat.set( worldPosition.x, worldPosition.y, worldPosition.z, 1 );
   1350 //        viewProjectionMatrix.mult(tmp_quat, tmp_quat);
   1351 //        tmp_quat.multLocal( 1.0f / tmp_quat.getW() );
   1352 //        store.x = ( ( tmp_quat.getX() + 1 ) * ( viewPortRight - viewPortLeft ) / 2 + viewPortLeft ) * getWidth();
   1353 //        store.y = ( ( tmp_quat.getY() + 1 ) * ( viewPortTop - viewPortBottom ) / 2 + viewPortBottom ) * getHeight();
   1354 //        store.z = ( tmp_quat.getZ() + 1 ) / 2;
   1355 //        vars.release();
   1356 
   1357         float w = viewProjectionMatrix.multProj(worldPosition, store);
   1358         store.divideLocal(w);
   1359 
   1360         store.x = ((store.x + 1f) * (viewPortRight - viewPortLeft) / 2f + viewPortLeft) * getWidth();
   1361         store.y = ((store.y + 1f) * (viewPortTop - viewPortBottom) / 2f + viewPortBottom) * getHeight();
   1362         store.z = (store.z + 1f) / 2f;
   1363 
   1364         return store;
   1365     }
   1366 
   1367     /**
   1368      * @return the width/resolution of the display.
   1369      */
   1370     public int getWidth() {
   1371         return width;
   1372     }
   1373 
   1374     /**
   1375      * @return the height/resolution of the display.
   1376      */
   1377     public int getHeight() {
   1378         return height;
   1379     }
   1380 
   1381     @Override
   1382     public String toString() {
   1383         return "Camera[location=" + location + "\n, direction=" + getDirection() + "\n"
   1384                 + "res=" + width + "x" + height + ", parallel=" + parallelProjection + "\n"
   1385                 + "near=" + frustumNear + ", far=" + frustumFar + "]";
   1386     }
   1387 
   1388     public void write(JmeExporter e) throws IOException {
   1389         OutputCapsule capsule = e.getCapsule(this);
   1390         capsule.write(location, "location", Vector3f.ZERO);
   1391         capsule.write(rotation, "rotation", Quaternion.DIRECTION_Z);
   1392         capsule.write(frustumNear, "frustumNear", 1);
   1393         capsule.write(frustumFar, "frustumFar", 2);
   1394         capsule.write(frustumLeft, "frustumLeft", -0.5f);
   1395         capsule.write(frustumRight, "frustumRight", 0.5f);
   1396         capsule.write(frustumTop, "frustumTop", 0.5f);
   1397         capsule.write(frustumBottom, "frustumBottom", -0.5f);
   1398         capsule.write(coeffLeft, "coeffLeft", new float[2]);
   1399         capsule.write(coeffRight, "coeffRight", new float[2]);
   1400         capsule.write(coeffBottom, "coeffBottom", new float[2]);
   1401         capsule.write(coeffTop, "coeffTop", new float[2]);
   1402         capsule.write(viewPortLeft, "viewPortLeft", 0);
   1403         capsule.write(viewPortRight, "viewPortRight", 1);
   1404         capsule.write(viewPortTop, "viewPortTop", 1);
   1405         capsule.write(viewPortBottom, "viewPortBottom", 0);
   1406         capsule.write(width, "width", 0);
   1407         capsule.write(height, "height", 0);
   1408         capsule.write(name, "name", null);
   1409     }
   1410 
   1411     public void read(JmeImporter e) throws IOException {
   1412         InputCapsule capsule = e.getCapsule(this);
   1413         location = (Vector3f) capsule.readSavable("location", Vector3f.ZERO.clone());
   1414         rotation = (Quaternion) capsule.readSavable("rotation", Quaternion.DIRECTION_Z.clone());
   1415         frustumNear = capsule.readFloat("frustumNear", 1);
   1416         frustumFar = capsule.readFloat("frustumFar", 2);
   1417         frustumLeft = capsule.readFloat("frustumLeft", -0.5f);
   1418         frustumRight = capsule.readFloat("frustumRight", 0.5f);
   1419         frustumTop = capsule.readFloat("frustumTop", 0.5f);
   1420         frustumBottom = capsule.readFloat("frustumBottom", -0.5f);
   1421         coeffLeft = capsule.readFloatArray("coeffLeft", new float[2]);
   1422         coeffRight = capsule.readFloatArray("coeffRight", new float[2]);
   1423         coeffBottom = capsule.readFloatArray("coeffBottom", new float[2]);
   1424         coeffTop = capsule.readFloatArray("coeffTop", new float[2]);
   1425         viewPortLeft = capsule.readFloat("viewPortLeft", 0);
   1426         viewPortRight = capsule.readFloat("viewPortRight", 1);
   1427         viewPortTop = capsule.readFloat("viewPortTop", 1);
   1428         viewPortBottom = capsule.readFloat("viewPortBottom", 0);
   1429         width = capsule.readInt("width", 1);
   1430         height = capsule.readInt("height", 1);
   1431         name = capsule.readString("name", null);
   1432         onFrustumChange();
   1433         onViewPortChange();
   1434         onFrameChange();
   1435     }
   1436 }
   1437