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      */
Camera()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      */
Camera(int width, int height)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
clone()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 	 */
copyFrom(Camera cam)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      */
setName(String name)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      */
getName()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      */
setClipPlane(Plane clipPlane, Plane.Side side)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      */
setClipPlane(Plane clipPlane)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      */
resize(int width, int height, boolean fixAspect)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      */
getFrustumBottom()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      */
setFrustumBottom(float frustumBottom)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      */
getFrustumFar()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      */
setFrustumFar(float frustumFar)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      */
getFrustumLeft()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      */
setFrustumLeft(float frustumLeft)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      */
getFrustumNear()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      */
setFrustumNear(float frustumNear)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      */
getFrustumRight()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      */
setFrustumRight(float frustumRight)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      */
getFrustumTop()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      */
setFrustumTop(float frustumTop)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      */
getLocation()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      */
getRotation()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      */
getDirection()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      */
getLeft()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      */
getUp()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      */
getDirection(Vector3f store)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      */
getLeft(Vector3f store)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      */
getUp(Vector3f store)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      */
setLocation(Vector3f location)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      */
setRotation(Quaternion rotation)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      */
lookAtDirection(Vector3f direction, Vector3f up)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      */
setAxes(Vector3f left, Vector3f up, Vector3f direction)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      */
setAxes(Quaternion axes)718     public void setAxes(Quaternion axes) {
719         this.rotation.set(axes);
720         onFrameChange();
721     }
722 
723     /**
724      * normalize normalizes the camera vectors.
725      */
normalize()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      */
setFrustum(float near, float far, float left, float right, float top, float bottom)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      */
setFrustumPerspective(float fovY, float aspect, float near, float far)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      */
setFrame(Vector3f location, Vector3f left, Vector3f up, Vector3f direction)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      */
lookAt(Vector3f pos, Vector3f worldUpVector)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      */
setFrame(Vector3f location, Quaternion axes)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      */
update()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      */
getPlaneState()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      */
setPlaneState(int planeState)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      */
getViewPortLeft()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      */
setViewPortLeft(float left)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      */
getViewPortRight()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      */
setViewPortRight(float right)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      */
getViewPortTop()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      */
setViewPortTop(float top)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      */
getViewPortBottom()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      */
setViewPortBottom(float bottom)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      */
setViewPort(float left, float right, float bottom, float top)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      */
distanceToNearPlane(Vector3f pos)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      */
contains(BoundingVolume bound)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      */
containsGui(BoundingVolume bound)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      */
getViewMatrix()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      */
setProjectionMatrix(Matrix4f projMatrix)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      */
getProjectionMatrix()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      */
updateViewProjection()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      */
getViewProjectionMatrix()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      */
isViewportChanged()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      */
clearViewportChanged()1132     public void clearViewportChanged() {
1133         viewportChanged = false;
1134     }
1135 
1136     /**
1137      * Called when the viewport has been changed.
1138      */
onViewPortChange()1139     public void onViewPortChange() {
1140         viewportChanged = true;
1141         setGuiBounding();
1142     }
1143 
setGuiBounding()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      */
onFrustumChange()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      */
onFrameChange()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      */
isParallelProjection()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      */
setParallelProjection(final boolean value)1293     public void setParallelProjection(final boolean value) {
1294         this.parallelProjection = value;
1295         onFrustumChange();
1296     }
1297 
1298     /**
1299      * @see Camera#getWorldCoordinates
1300      */
getWorldCoordinates(Vector2f screenPos, float zPos)1301     public Vector3f getWorldCoordinates(Vector2f screenPos, float zPos) {
1302         return getWorldCoordinates(screenPos, zPos, null);
1303     }
1304 
1305     /**
1306      * @see Camera#getWorldCoordinates
1307      */
getWorldCoordinates(Vector2f screenPosition, float zPos, Vector3f store)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      */
getScreenCoordinates(Vector3f worldPos)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      */
getScreenCoordinates(Vector3f worldPosition, Vector3f store)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      */
getWidth()1370     public int getWidth() {
1371         return width;
1372     }
1373 
1374     /**
1375      * @return the height/resolution of the display.
1376      */
getHeight()1377     public int getHeight() {
1378         return height;
1379     }
1380 
1381     @Override
toString()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 
write(JmeExporter e)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 
read(JmeImporter e)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