| /* |
| * Copyright (c) 2009-2010 jMonkeyEngine |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package com.jme3.renderer; |
| |
| import com.jme3.bounding.BoundingBox; |
| import com.jme3.bounding.BoundingVolume; |
| import com.jme3.export.*; |
| import com.jme3.math.*; |
| import com.jme3.util.TempVars; |
| import java.io.IOException; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** |
| * <code>Camera</code> is a standalone, purely mathematical class for doing |
| * camera-related computations. |
| * |
| * <p> |
| * Given input data such as location, orientation (direction, left, up), |
| * and viewport settings, it can compute data necessary to render objects |
| * with the graphics library. Two matrices are generated, the view matrix |
| * transforms objects from world space into eye space, while the projection |
| * matrix transforms objects from eye space into clip space. |
| * </p> |
| * <p>Another purpose of the camera class is to do frustum culling operations, |
| * defined by six planes which define a 3D frustum shape, it is possible to |
| * test if an object bounded by a mathematically defined volume is inside |
| * the camera frustum, and thus to avoid rendering objects that are outside |
| * the frustum |
| * </p> |
| * |
| * @author Mark Powell |
| * @author Joshua Slack |
| */ |
| public class Camera implements Savable, Cloneable { |
| |
| private static final Logger logger = Logger.getLogger(Camera.class.getName()); |
| |
| /** |
| * The <code>FrustumIntersect</code> enum is returned as a result |
| * of a culling check operation, |
| * see {@link #contains(com.jme3.bounding.BoundingVolume) } |
| */ |
| public enum FrustumIntersect { |
| |
| /** |
| * defines a constant assigned to spatials that are completely outside |
| * of this camera's view frustum. |
| */ |
| Outside, |
| /** |
| * defines a constant assigned to spatials that are completely inside |
| * the camera's view frustum. |
| */ |
| Inside, |
| /** |
| * defines a constant assigned to spatials that are intersecting one of |
| * the six planes that define the view frustum. |
| */ |
| Intersects; |
| } |
| /** |
| * LEFT_PLANE represents the left plane of the camera frustum. |
| */ |
| private static final int LEFT_PLANE = 0; |
| /** |
| * RIGHT_PLANE represents the right plane of the camera frustum. |
| */ |
| private static final int RIGHT_PLANE = 1; |
| /** |
| * BOTTOM_PLANE represents the bottom plane of the camera frustum. |
| */ |
| private static final int BOTTOM_PLANE = 2; |
| /** |
| * TOP_PLANE represents the top plane of the camera frustum. |
| */ |
| private static final int TOP_PLANE = 3; |
| /** |
| * FAR_PLANE represents the far plane of the camera frustum. |
| */ |
| private static final int FAR_PLANE = 4; |
| /** |
| * NEAR_PLANE represents the near plane of the camera frustum. |
| */ |
| private static final int NEAR_PLANE = 5; |
| /** |
| * FRUSTUM_PLANES represents the number of planes of the camera frustum. |
| */ |
| private static final int FRUSTUM_PLANES = 6; |
| /** |
| * MAX_WORLD_PLANES holds the maximum planes allowed by the system. |
| */ |
| private static final int MAX_WORLD_PLANES = 6; |
| /** |
| * Camera's location |
| */ |
| protected Vector3f location; |
| /** |
| * The orientation of the camera. |
| */ |
| protected Quaternion rotation; |
| /** |
| * Distance from camera to near frustum plane. |
| */ |
| protected float frustumNear; |
| /** |
| * Distance from camera to far frustum plane. |
| */ |
| protected float frustumFar; |
| /** |
| * Distance from camera to left frustum plane. |
| */ |
| protected float frustumLeft; |
| /** |
| * Distance from camera to right frustum plane. |
| */ |
| protected float frustumRight; |
| /** |
| * Distance from camera to top frustum plane. |
| */ |
| protected float frustumTop; |
| /** |
| * Distance from camera to bottom frustum plane. |
| */ |
| protected float frustumBottom; |
| //Temporary values computed in onFrustumChange that are needed if a |
| //call is made to onFrameChange. |
| protected float[] coeffLeft; |
| protected float[] coeffRight; |
| protected float[] coeffBottom; |
| protected float[] coeffTop; |
| //view port coordinates |
| /** |
| * Percent value on display where horizontal viewing starts for this camera. |
| * Default is 0. |
| */ |
| protected float viewPortLeft; |
| /** |
| * Percent value on display where horizontal viewing ends for this camera. |
| * Default is 1. |
| */ |
| protected float viewPortRight; |
| /** |
| * Percent value on display where vertical viewing ends for this camera. |
| * Default is 1. |
| */ |
| protected float viewPortTop; |
| /** |
| * Percent value on display where vertical viewing begins for this camera. |
| * Default is 0. |
| */ |
| protected float viewPortBottom; |
| /** |
| * Array holding the planes that this camera will check for culling. |
| */ |
| protected Plane[] worldPlane; |
| /** |
| * A mask value set during contains() that allows fast culling of a Node's |
| * children. |
| */ |
| private int planeState; |
| protected int width; |
| protected int height; |
| protected boolean viewportChanged = true; |
| /** |
| * store the value for field parallelProjection |
| */ |
| private boolean parallelProjection; |
| protected Matrix4f projectionMatrixOverride; |
| protected Matrix4f viewMatrix = new Matrix4f(); |
| protected Matrix4f projectionMatrix = new Matrix4f(); |
| protected Matrix4f viewProjectionMatrix = new Matrix4f(); |
| private BoundingBox guiBounding = new BoundingBox(); |
| /** The camera's name. */ |
| protected String name; |
| |
| /** |
| * Serialization only. Do not use. |
| */ |
| public Camera() { |
| worldPlane = new Plane[MAX_WORLD_PLANES]; |
| for (int i = 0; i < MAX_WORLD_PLANES; i++) { |
| worldPlane[i] = new Plane(); |
| } |
| } |
| |
| /** |
| * Constructor instantiates a new <code>Camera</code> object. All |
| * values of the camera are set to default. |
| */ |
| public Camera(int width, int height) { |
| this(); |
| location = new Vector3f(); |
| rotation = new Quaternion(); |
| |
| frustumNear = 1.0f; |
| frustumFar = 2.0f; |
| frustumLeft = -0.5f; |
| frustumRight = 0.5f; |
| frustumTop = 0.5f; |
| frustumBottom = -0.5f; |
| |
| coeffLeft = new float[2]; |
| coeffRight = new float[2]; |
| coeffBottom = new float[2]; |
| coeffTop = new float[2]; |
| |
| viewPortLeft = 0.0f; |
| viewPortRight = 1.0f; |
| viewPortTop = 1.0f; |
| viewPortBottom = 0.0f; |
| |
| this.width = width; |
| this.height = height; |
| |
| onFrustumChange(); |
| onViewPortChange(); |
| onFrameChange(); |
| |
| logger.log(Level.INFO, "Camera created (W: {0}, H: {1})", new Object[]{width, height}); |
| } |
| |
| @Override |
| public Camera clone() { |
| try { |
| Camera cam = (Camera) super.clone(); |
| cam.viewportChanged = true; |
| cam.planeState = 0; |
| |
| cam.worldPlane = new Plane[MAX_WORLD_PLANES]; |
| for (int i = 0; i < worldPlane.length; i++) { |
| cam.worldPlane[i] = worldPlane[i].clone(); |
| } |
| |
| cam.coeffLeft = new float[2]; |
| cam.coeffRight = new float[2]; |
| cam.coeffBottom = new float[2]; |
| cam.coeffTop = new float[2]; |
| |
| cam.location = location.clone(); |
| cam.rotation = rotation.clone(); |
| |
| if (projectionMatrixOverride != null) { |
| cam.projectionMatrixOverride = projectionMatrixOverride.clone(); |
| } |
| |
| cam.viewMatrix = viewMatrix.clone(); |
| cam.projectionMatrix = projectionMatrix.clone(); |
| cam.viewProjectionMatrix = viewProjectionMatrix.clone(); |
| cam.guiBounding = (BoundingBox) guiBounding.clone(); |
| |
| cam.update(); |
| |
| return cam; |
| } catch (CloneNotSupportedException ex) { |
| throw new AssertionError(); |
| } |
| } |
| |
| /** |
| * This method copise the settings of the given camera. |
| * |
| * @param cam |
| * the camera we copy the settings from |
| */ |
| public void copyFrom(Camera cam) { |
| location.set(cam.location); |
| rotation.set(cam.rotation); |
| |
| frustumNear = cam.frustumNear; |
| frustumFar = cam.frustumFar; |
| frustumLeft = cam.frustumLeft; |
| frustumRight = cam.frustumRight; |
| frustumTop = cam.frustumTop; |
| frustumBottom = cam.frustumBottom; |
| |
| coeffLeft[0] = cam.coeffLeft[0]; |
| coeffLeft[1] = cam.coeffLeft[1]; |
| coeffRight[0] = cam.coeffRight[0]; |
| coeffRight[1] = cam.coeffRight[1]; |
| coeffBottom[0] = cam.coeffBottom[0]; |
| coeffBottom[1] = cam.coeffBottom[1]; |
| coeffTop[0] = cam.coeffTop[0]; |
| coeffTop[1] = cam.coeffTop[1]; |
| |
| viewPortLeft = cam.viewPortLeft; |
| viewPortRight = cam.viewPortRight; |
| viewPortTop = cam.viewPortTop; |
| viewPortBottom = cam.viewPortBottom; |
| |
| this.width = cam.width; |
| this.height = cam.height; |
| |
| this.planeState = cam.planeState; |
| this.viewportChanged = cam.viewportChanged; |
| for (int i = 0; i < MAX_WORLD_PLANES; ++i) { |
| worldPlane[i].setNormal(cam.worldPlane[i].getNormal()); |
| worldPlane[i].setConstant(cam.worldPlane[i].getConstant()); |
| } |
| |
| this.parallelProjection = cam.parallelProjection; |
| if(cam.projectionMatrixOverride != null) { |
| if(this.projectionMatrixOverride == null) { |
| this.projectionMatrixOverride = cam.projectionMatrixOverride.clone(); |
| } else { |
| this.projectionMatrixOverride.set(cam.projectionMatrixOverride); |
| } |
| } else { |
| this.projectionMatrixOverride = null; |
| } |
| this.viewMatrix.set(cam.viewMatrix); |
| this.projectionMatrix.set(cam.projectionMatrix); |
| this.viewProjectionMatrix.set(cam.viewProjectionMatrix); |
| |
| this.guiBounding.setXExtent(cam.guiBounding.getXExtent()); |
| this.guiBounding.setYExtent(cam.guiBounding.getYExtent()); |
| this.guiBounding.setZExtent(cam.guiBounding.getZExtent()); |
| this.guiBounding.setCenter(cam.guiBounding.getCenter()); |
| this.guiBounding.setCheckPlane(cam.guiBounding.getCheckPlane()); |
| |
| this.name = cam.name; |
| } |
| |
| /** |
| * This method sets the cameras name. |
| * @param name the cameras name |
| */ |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| /** |
| * This method returns the cameras name. |
| * @return the cameras name |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * Sets a clipPlane for this camera. |
| * The cliPlane is used to recompute the projectionMatrix using the plane as the near plane |
| * This technique is known as the oblique near-plane clipping method introduced by Eric Lengyel |
| * more info here |
| * <ul> |
| * <li><a href="http://www.terathon.com/code/oblique.html">http://www.terathon.com/code/oblique.html</a> |
| * <li><a href="http://aras-p.info/texts/obliqueortho.html">http://aras-p.info/texts/obliqueortho.html</a> |
| * <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> |
| * </ul> |
| * |
| * 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. |
| * if you want to handle the sky bucket, look at how it's done in SimpleWaterProcessor.java |
| * @param clipPlane the plane |
| * @param side the side the camera stands from the plane |
| */ |
| public void setClipPlane(Plane clipPlane, Plane.Side side) { |
| float sideFactor = 1; |
| if (side == Plane.Side.Negative) { |
| sideFactor = -1; |
| } |
| //we are on the other side of the plane no need to clip anymore. |
| if (clipPlane.whichSide(location) == side) { |
| return; |
| } |
| Matrix4f p = projectionMatrix.clone(); |
| |
| Matrix4f ivm = viewMatrix.clone(); |
| |
| Vector3f point = clipPlane.getNormal().mult(clipPlane.getConstant()); |
| Vector3f pp = ivm.mult(point); |
| Vector3f pn = ivm.multNormal(clipPlane.getNormal(), null); |
| Vector4f clipPlaneV = new Vector4f(pn.x * sideFactor, pn.y * sideFactor, pn.z * sideFactor, -(pp.dot(pn)) * sideFactor); |
| |
| Vector4f v = new Vector4f(0, 0, 0, 0); |
| |
| v.x = (Math.signum(clipPlaneV.x) + p.m02) / p.m00; |
| v.y = (Math.signum(clipPlaneV.y) + p.m12) / p.m11; |
| v.z = -1.0f; |
| v.w = (1.0f + p.m22) / p.m23; |
| |
| float dot = clipPlaneV.dot(v);//clipPlaneV.x * v.x + clipPlaneV.y * v.y + clipPlaneV.z * v.z + clipPlaneV.w * v.w; |
| Vector4f c = clipPlaneV.mult(2.0f / dot); |
| |
| p.m20 = c.x - p.m30; |
| p.m21 = c.y - p.m31; |
| p.m22 = c.z - p.m32; |
| p.m23 = c.w - p.m33; |
| setProjectionMatrix(p); |
| } |
| |
| /** |
| * Sets a clipPlane for this camera. |
| * The cliPlane is used to recompute the projectionMatrix using the plane as the near plane |
| * This technique is known as the oblique near-plane clipping method introduced by Eric Lengyel |
| * more info here |
| * <ul> |
| * <li><a href="http://www.terathon.com/code/oblique.html">http://www.terathon.com/code/oblique.html</a></li> |
| * <li><a href="http://aras-p.info/texts/obliqueortho.html">http://aras-p.info/texts/obliqueortho.html</a></li> |
| * <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></li> |
| * </ul> |
| * |
| * 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. |
| * if you want to handle the sky bucket, look at how it's done in SimpleWaterProcessor.java |
| * @param clipPlane the plane |
| */ |
| public void setClipPlane(Plane clipPlane) { |
| setClipPlane(clipPlane, clipPlane.whichSide(location)); |
| } |
| |
| /** |
| * Resizes this camera's view with the given width and height. This is |
| * similar to constructing a new camera, but reusing the same Object. This |
| * method is called by an associated {@link RenderManager} to notify the camera of |
| * changes in the display dimensions. |
| * |
| * @param width the view width |
| * @param height the view height |
| * @param fixAspect If true, the camera's aspect ratio will be recomputed. |
| * Recomputing the aspect ratio requires changing the frustum values. |
| */ |
| public void resize(int width, int height, boolean fixAspect) { |
| this.width = width; |
| this.height = height; |
| onViewPortChange(); |
| |
| if (fixAspect /*&& !parallelProjection*/) { |
| frustumRight = frustumTop * ((float) width / height); |
| frustumLeft = -frustumRight; |
| onFrustumChange(); |
| } |
| } |
| |
| /** |
| * <code>getFrustumBottom</code> returns the value of the bottom frustum |
| * plane. |
| * |
| * @return the value of the bottom frustum plane. |
| */ |
| public float getFrustumBottom() { |
| return frustumBottom; |
| } |
| |
| /** |
| * <code>setFrustumBottom</code> sets the value of the bottom frustum |
| * plane. |
| * |
| * @param frustumBottom the value of the bottom frustum plane. |
| */ |
| public void setFrustumBottom(float frustumBottom) { |
| this.frustumBottom = frustumBottom; |
| onFrustumChange(); |
| } |
| |
| /** |
| * <code>getFrustumFar</code> gets the value of the far frustum plane. |
| * |
| * @return the value of the far frustum plane. |
| */ |
| public float getFrustumFar() { |
| return frustumFar; |
| } |
| |
| /** |
| * <code>setFrustumFar</code> sets the value of the far frustum plane. |
| * |
| * @param frustumFar the value of the far frustum plane. |
| */ |
| public void setFrustumFar(float frustumFar) { |
| this.frustumFar = frustumFar; |
| onFrustumChange(); |
| } |
| |
| /** |
| * <code>getFrustumLeft</code> gets the value of the left frustum plane. |
| * |
| * @return the value of the left frustum plane. |
| */ |
| public float getFrustumLeft() { |
| return frustumLeft; |
| } |
| |
| /** |
| * <code>setFrustumLeft</code> sets the value of the left frustum plane. |
| * |
| * @param frustumLeft the value of the left frustum plane. |
| */ |
| public void setFrustumLeft(float frustumLeft) { |
| this.frustumLeft = frustumLeft; |
| onFrustumChange(); |
| } |
| |
| /** |
| * <code>getFrustumNear</code> gets the value of the near frustum plane. |
| * |
| * @return the value of the near frustum plane. |
| */ |
| public float getFrustumNear() { |
| return frustumNear; |
| } |
| |
| /** |
| * <code>setFrustumNear</code> sets the value of the near frustum plane. |
| * |
| * @param frustumNear the value of the near frustum plane. |
| */ |
| public void setFrustumNear(float frustumNear) { |
| this.frustumNear = frustumNear; |
| onFrustumChange(); |
| } |
| |
| /** |
| * <code>getFrustumRight</code> gets the value of the right frustum plane. |
| * |
| * @return frustumRight the value of the right frustum plane. |
| */ |
| public float getFrustumRight() { |
| return frustumRight; |
| } |
| |
| /** |
| * <code>setFrustumRight</code> sets the value of the right frustum plane. |
| * |
| * @param frustumRight the value of the right frustum plane. |
| */ |
| public void setFrustumRight(float frustumRight) { |
| this.frustumRight = frustumRight; |
| onFrustumChange(); |
| } |
| |
| /** |
| * <code>getFrustumTop</code> gets the value of the top frustum plane. |
| * |
| * @return the value of the top frustum plane. |
| */ |
| public float getFrustumTop() { |
| return frustumTop; |
| } |
| |
| /** |
| * <code>setFrustumTop</code> sets the value of the top frustum plane. |
| * |
| * @param frustumTop the value of the top frustum plane. |
| */ |
| public void setFrustumTop(float frustumTop) { |
| this.frustumTop = frustumTop; |
| onFrustumChange(); |
| } |
| |
| /** |
| * <code>getLocation</code> retrieves the location vector of the camera. |
| * |
| * @return the position of the camera. |
| * @see Camera#getLocation() |
| */ |
| public Vector3f getLocation() { |
| return location; |
| } |
| |
| /** |
| * <code>getRotation</code> retrieves the rotation quaternion of the camera. |
| * |
| * @return the rotation of the camera. |
| */ |
| public Quaternion getRotation() { |
| return rotation; |
| } |
| |
| /** |
| * <code>getDirection</code> retrieves the direction vector the camera is |
| * facing. |
| * |
| * @return the direction the camera is facing. |
| * @see Camera#getDirection() |
| */ |
| public Vector3f getDirection() { |
| return rotation.getRotationColumn(2); |
| } |
| |
| /** |
| * <code>getLeft</code> retrieves the left axis of the camera. |
| * |
| * @return the left axis of the camera. |
| * @see Camera#getLeft() |
| */ |
| public Vector3f getLeft() { |
| return rotation.getRotationColumn(0); |
| } |
| |
| /** |
| * <code>getUp</code> retrieves the up axis of the camera. |
| * |
| * @return the up axis of the camera. |
| * @see Camera#getUp() |
| */ |
| public Vector3f getUp() { |
| return rotation.getRotationColumn(1); |
| } |
| |
| /** |
| * <code>getDirection</code> retrieves the direction vector the camera is |
| * facing. |
| * |
| * @return the direction the camera is facing. |
| * @see Camera#getDirection() |
| */ |
| public Vector3f getDirection(Vector3f store) { |
| return rotation.getRotationColumn(2, store); |
| } |
| |
| /** |
| * <code>getLeft</code> retrieves the left axis of the camera. |
| * |
| * @return the left axis of the camera. |
| * @see Camera#getLeft() |
| */ |
| public Vector3f getLeft(Vector3f store) { |
| return rotation.getRotationColumn(0, store); |
| } |
| |
| /** |
| * <code>getUp</code> retrieves the up axis of the camera. |
| * |
| * @return the up axis of the camera. |
| * @see Camera#getUp() |
| */ |
| public Vector3f getUp(Vector3f store) { |
| return rotation.getRotationColumn(1, store); |
| } |
| |
| /** |
| * <code>setLocation</code> sets the position of the camera. |
| * |
| * @param location the position of the camera. |
| */ |
| public void setLocation(Vector3f location) { |
| this.location.set(location); |
| onFrameChange(); |
| } |
| |
| /** |
| * <code>setRotation</code> sets the orientation of this camera. |
| * This will be equivelant to setting each of the axes: |
| * <code><br> |
| * cam.setLeft(rotation.getRotationColumn(0));<br> |
| * cam.setUp(rotation.getRotationColumn(1));<br> |
| * cam.setDirection(rotation.getRotationColumn(2));<br> |
| * </code> |
| * |
| * @param rotation the rotation of this camera |
| */ |
| public void setRotation(Quaternion rotation) { |
| this.rotation.set(rotation); |
| onFrameChange(); |
| } |
| |
| /** |
| * <code>lookAtDirection</code> sets the direction the camera is facing |
| * given a direction and an up vector. |
| * |
| * @param direction the direction this camera is facing. |
| */ |
| public void lookAtDirection(Vector3f direction, Vector3f up) { |
| this.rotation.lookAt(direction, up); |
| onFrameChange(); |
| } |
| |
| /** |
| * <code>setAxes</code> sets the axes (left, up and direction) for this |
| * camera. |
| * |
| * @param left the left axis of the camera. |
| * @param up the up axis of the camera. |
| * @param direction the direction the camera is facing. |
| * |
| * @see Camera#setAxes(com.jme3.math.Quaternion) |
| */ |
| public void setAxes(Vector3f left, Vector3f up, Vector3f direction) { |
| this.rotation.fromAxes(left, up, direction); |
| onFrameChange(); |
| } |
| |
| /** |
| * <code>setAxes</code> uses a rotational matrix to set the axes of the |
| * camera. |
| * |
| * @param axes the matrix that defines the orientation of the camera. |
| */ |
| public void setAxes(Quaternion axes) { |
| this.rotation.set(axes); |
| onFrameChange(); |
| } |
| |
| /** |
| * normalize normalizes the camera vectors. |
| */ |
| public void normalize() { |
| this.rotation.normalizeLocal(); |
| onFrameChange(); |
| } |
| |
| /** |
| * <code>setFrustum</code> sets the frustum of this camera object. |
| * |
| * @param near the near plane. |
| * @param far the far plane. |
| * @param left the left plane. |
| * @param right the right plane. |
| * @param top the top plane. |
| * @param bottom the bottom plane. |
| * @see Camera#setFrustum(float, float, float, float, |
| * float, float) |
| */ |
| public void setFrustum(float near, float far, float left, float right, |
| float top, float bottom) { |
| |
| frustumNear = near; |
| frustumFar = far; |
| frustumLeft = left; |
| frustumRight = right; |
| frustumTop = top; |
| frustumBottom = bottom; |
| onFrustumChange(); |
| } |
| |
| /** |
| * <code>setFrustumPerspective</code> defines the frustum for the camera. This |
| * frustum is defined by a viewing angle, aspect ratio, and near/far planes |
| * |
| * @param fovY Frame of view angle along the Y in degrees. |
| * @param aspect Width:Height ratio |
| * @param near Near view plane distance |
| * @param far Far view plane distance |
| */ |
| public void setFrustumPerspective(float fovY, float aspect, float near, |
| float far) { |
| if (Float.isNaN(aspect) || Float.isInfinite(aspect)) { |
| // ignore. |
| logger.log(Level.WARNING, "Invalid aspect given to setFrustumPerspective: {0}", aspect); |
| return; |
| } |
| |
| float h = FastMath.tan(fovY * FastMath.DEG_TO_RAD * .5f) * near; |
| float w = h * aspect; |
| frustumLeft = -w; |
| frustumRight = w; |
| frustumBottom = -h; |
| frustumTop = h; |
| frustumNear = near; |
| frustumFar = far; |
| |
| onFrustumChange(); |
| } |
| |
| /** |
| * <code>setFrame</code> sets the orientation and location of the camera. |
| * |
| * @param location the point position of the camera. |
| * @param left the left axis of the camera. |
| * @param up the up axis of the camera. |
| * @param direction the facing of the camera. |
| * @see Camera#setFrame(com.jme3.math.Vector3f, |
| * com.jme3.math.Vector3f, com.jme3.math.Vector3f, com.jme3.math.Vector3f) |
| */ |
| public void setFrame(Vector3f location, Vector3f left, Vector3f up, |
| Vector3f direction) { |
| |
| this.location = location; |
| this.rotation.fromAxes(left, up, direction); |
| onFrameChange(); |
| } |
| |
| /** |
| * <code>lookAt</code> is a convienence method for auto-setting the frame |
| * based on a world position the user desires the camera to look at. It |
| * repoints the camera towards the given position using the difference |
| * between the position and the current camera location as a direction |
| * vector and the worldUpVector to compute up and left camera vectors. |
| * |
| * @param pos where to look at in terms of world coordinates |
| * @param worldUpVector a normalized vector indicating the up direction of the world. |
| * (typically {0, 1, 0} in jME.) |
| */ |
| public void lookAt(Vector3f pos, Vector3f worldUpVector) { |
| TempVars vars = TempVars.get(); |
| Vector3f newDirection = vars.vect1; |
| Vector3f newUp = vars.vect2; |
| Vector3f newLeft = vars.vect3; |
| |
| newDirection.set(pos).subtractLocal(location).normalizeLocal(); |
| |
| newUp.set(worldUpVector).normalizeLocal(); |
| if (newUp.equals(Vector3f.ZERO)) { |
| newUp.set(Vector3f.UNIT_Y); |
| } |
| |
| newLeft.set(newUp).crossLocal(newDirection).normalizeLocal(); |
| if (newLeft.equals(Vector3f.ZERO)) { |
| if (newDirection.x != 0) { |
| newLeft.set(newDirection.y, -newDirection.x, 0f); |
| } else { |
| newLeft.set(0f, newDirection.z, -newDirection.y); |
| } |
| } |
| |
| newUp.set(newDirection).crossLocal(newLeft).normalizeLocal(); |
| |
| this.rotation.fromAxes(newLeft, newUp, newDirection); |
| this.rotation.normalizeLocal(); |
| vars.release(); |
| |
| onFrameChange(); |
| } |
| |
| /** |
| * <code>setFrame</code> sets the orientation and location of the camera. |
| * |
| * @param location |
| * the point position of the camera. |
| * @param axes |
| * the orientation of the camera. |
| */ |
| public void setFrame(Vector3f location, Quaternion axes) { |
| this.location = location; |
| this.rotation.set(axes); |
| onFrameChange(); |
| } |
| |
| /** |
| * <code>update</code> updates the camera parameters by calling |
| * <code>onFrustumChange</code>,<code>onViewPortChange</code> and |
| * <code>onFrameChange</code>. |
| * |
| * @see Camera#update() |
| */ |
| public void update() { |
| onFrustumChange(); |
| onViewPortChange(); |
| onFrameChange(); |
| } |
| |
| /** |
| * <code>getPlaneState</code> returns the state of the frustum planes. So |
| * checks can be made as to which frustum plane has been examined for |
| * culling thus far. |
| * |
| * @return the current plane state int. |
| */ |
| public int getPlaneState() { |
| return planeState; |
| } |
| |
| /** |
| * <code>setPlaneState</code> sets the state to keep track of tested |
| * planes for culling. |
| * |
| * @param planeState the updated state. |
| */ |
| public void setPlaneState(int planeState) { |
| this.planeState = planeState; |
| } |
| |
| /** |
| * <code>getViewPortLeft</code> gets the left boundary of the viewport |
| * |
| * @return the left boundary of the viewport |
| */ |
| public float getViewPortLeft() { |
| return viewPortLeft; |
| } |
| |
| /** |
| * <code>setViewPortLeft</code> sets the left boundary of the viewport |
| * |
| * @param left the left boundary of the viewport |
| */ |
| public void setViewPortLeft(float left) { |
| viewPortLeft = left; |
| onViewPortChange(); |
| } |
| |
| /** |
| * <code>getViewPortRight</code> gets the right boundary of the viewport |
| * |
| * @return the right boundary of the viewport |
| */ |
| public float getViewPortRight() { |
| return viewPortRight; |
| } |
| |
| /** |
| * <code>setViewPortRight</code> sets the right boundary of the viewport |
| * |
| * @param right the right boundary of the viewport |
| */ |
| public void setViewPortRight(float right) { |
| viewPortRight = right; |
| onViewPortChange(); |
| } |
| |
| /** |
| * <code>getViewPortTop</code> gets the top boundary of the viewport |
| * |
| * @return the top boundary of the viewport |
| */ |
| public float getViewPortTop() { |
| return viewPortTop; |
| } |
| |
| /** |
| * <code>setViewPortTop</code> sets the top boundary of the viewport |
| * |
| * @param top the top boundary of the viewport |
| */ |
| public void setViewPortTop(float top) { |
| viewPortTop = top; |
| onViewPortChange(); |
| } |
| |
| /** |
| * <code>getViewPortBottom</code> gets the bottom boundary of the viewport |
| * |
| * @return the bottom boundary of the viewport |
| */ |
| public float getViewPortBottom() { |
| return viewPortBottom; |
| } |
| |
| /** |
| * <code>setViewPortBottom</code> sets the bottom boundary of the viewport |
| * |
| * @param bottom the bottom boundary of the viewport |
| */ |
| public void setViewPortBottom(float bottom) { |
| viewPortBottom = bottom; |
| onViewPortChange(); |
| } |
| |
| /** |
| * <code>setViewPort</code> sets the boundaries of the viewport |
| * |
| * @param left the left boundary of the viewport (default: 0) |
| * @param right the right boundary of the viewport (default: 1) |
| * @param bottom the bottom boundary of the viewport (default: 0) |
| * @param top the top boundary of the viewport (default: 1) |
| */ |
| public void setViewPort(float left, float right, float bottom, float top) { |
| this.viewPortLeft = left; |
| this.viewPortRight = right; |
| this.viewPortBottom = bottom; |
| this.viewPortTop = top; |
| onViewPortChange(); |
| } |
| |
| /** |
| * Returns the pseudo distance from the given position to the near |
| * plane of the camera. This is used for render queue sorting. |
| * @param pos The position to compute a distance to. |
| * @return Distance from the far plane to the point. |
| */ |
| public float distanceToNearPlane(Vector3f pos) { |
| return worldPlane[NEAR_PLANE].pseudoDistance(pos); |
| } |
| |
| /** |
| * <code>contains</code> tests a bounding volume against the planes of the |
| * camera's frustum. The frustums planes are set such that the normals all |
| * face in towards the viewable scene. Therefore, if the bounding volume is |
| * on the negative side of the plane is can be culled out. |
| * |
| * NOTE: This method is used internally for culling, for public usage, |
| * the plane state of the bounding volume must be saved and restored, e.g: |
| * <code>BoundingVolume bv;<br/> |
| * Camera c;<br/> |
| * int planeState = bv.getPlaneState();<br/> |
| * bv.setPlaneState(0);<br/> |
| * c.contains(bv);<br/> |
| * bv.setPlaneState(plateState);<br/> |
| * </code> |
| * |
| * @param bound the bound to check for culling |
| * @return See enums in <code>FrustumIntersect</code> |
| */ |
| public FrustumIntersect contains(BoundingVolume bound) { |
| if (bound == null) { |
| return FrustumIntersect.Inside; |
| } |
| |
| int mask; |
| FrustumIntersect rVal = FrustumIntersect.Inside; |
| |
| for (int planeCounter = FRUSTUM_PLANES; planeCounter >= 0; planeCounter--) { |
| if (planeCounter == bound.getCheckPlane()) { |
| continue; // we have already checked this plane at first iteration |
| } |
| int planeId = (planeCounter == FRUSTUM_PLANES) ? bound.getCheckPlane() : planeCounter; |
| // int planeId = planeCounter; |
| |
| mask = 1 << (planeId); |
| if ((planeState & mask) == 0) { |
| Plane.Side side = bound.whichSide(worldPlane[planeId]); |
| |
| if (side == Plane.Side.Negative) { |
| //object is outside of frustum |
| bound.setCheckPlane(planeId); |
| return FrustumIntersect.Outside; |
| } else if (side == Plane.Side.Positive) { |
| //object is visible on *this* plane, so mark this plane |
| //so that we don't check it for sub nodes. |
| planeState |= mask; |
| } else { |
| rVal = FrustumIntersect.Intersects; |
| } |
| } |
| } |
| |
| return rVal; |
| } |
| |
| /** |
| * <code>containsGui</code> tests a bounding volume against the ortho |
| * bounding box of the camera. A bounding box spanning from |
| * 0, 0 to Width, Height. Constrained by the viewport settings on the |
| * camera. |
| * |
| * @param bound the bound to check for culling |
| * @return True if the camera contains the gui element bounding volume. |
| */ |
| public boolean containsGui(BoundingVolume bound) { |
| return guiBounding.intersects(bound); |
| } |
| |
| /** |
| * @return the view matrix of the camera. |
| * The view matrix transforms world space into eye space. |
| * This matrix is usually defined by the position and |
| * orientation of the camera. |
| */ |
| public Matrix4f getViewMatrix() { |
| return viewMatrix; |
| } |
| |
| /** |
| * Overrides the projection matrix used by the camera. Will |
| * use the matrix for computing the view projection matrix as well. |
| * Use null argument to return to normal functionality. |
| * |
| * @param projMatrix |
| */ |
| public void setProjectionMatrix(Matrix4f projMatrix) { |
| projectionMatrixOverride = projMatrix; |
| updateViewProjection(); |
| } |
| |
| /** |
| * @return the projection matrix of the camera. |
| * The view projection matrix transforms eye space into clip space. |
| * This matrix is usually defined by the viewport and perspective settings |
| * of the camera. |
| */ |
| public Matrix4f getProjectionMatrix() { |
| if (projectionMatrixOverride != null) { |
| return projectionMatrixOverride; |
| } |
| |
| return projectionMatrix; |
| } |
| |
| /** |
| * Updates the view projection matrix. |
| */ |
| public void updateViewProjection() { |
| if (projectionMatrixOverride != null) { |
| viewProjectionMatrix.set(projectionMatrixOverride).multLocal(viewMatrix); |
| } else { |
| //viewProjectionMatrix.set(viewMatrix).multLocal(projectionMatrix); |
| viewProjectionMatrix.set(projectionMatrix).multLocal(viewMatrix); |
| } |
| } |
| |
| /** |
| * @return The result of multiplying the projection matrix by the view |
| * matrix. This matrix is required for rendering an object. It is |
| * precomputed so as to not compute it every time an object is rendered. |
| */ |
| public Matrix4f getViewProjectionMatrix() { |
| return viewProjectionMatrix; |
| } |
| |
| /** |
| * @return True if the viewport (width, height, left, right, bottom, up) |
| * has been changed. This is needed in the renderer so that the proper |
| * viewport can be set-up. |
| */ |
| public boolean isViewportChanged() { |
| return viewportChanged; |
| } |
| |
| /** |
| * Clears the viewport changed flag once it has been updated inside |
| * the renderer. |
| */ |
| public void clearViewportChanged() { |
| viewportChanged = false; |
| } |
| |
| /** |
| * Called when the viewport has been changed. |
| */ |
| public void onViewPortChange() { |
| viewportChanged = true; |
| setGuiBounding(); |
| } |
| |
| private void setGuiBounding() { |
| float sx = width * viewPortLeft; |
| float ex = width * viewPortRight; |
| float sy = height * viewPortBottom; |
| float ey = height * viewPortTop; |
| float xExtent = Math.max(0f, (ex - sx) / 2f); |
| float yExtent = Math.max(0f, (ey - sy) / 2f); |
| guiBounding.setCenter(new Vector3f(sx + xExtent, sy + yExtent, 0)); |
| guiBounding.setXExtent(xExtent); |
| guiBounding.setYExtent(yExtent); |
| guiBounding.setZExtent(Float.MAX_VALUE); |
| } |
| |
| /** |
| * <code>onFrustumChange</code> updates the frustum to reflect any changes |
| * made to the planes. The new frustum values are kept in a temporary |
| * location for use when calculating the new frame. The projection |
| * matrix is updated to reflect the current values of the frustum. |
| */ |
| public void onFrustumChange() { |
| if (!isParallelProjection()) { |
| float nearSquared = frustumNear * frustumNear; |
| float leftSquared = frustumLeft * frustumLeft; |
| float rightSquared = frustumRight * frustumRight; |
| float bottomSquared = frustumBottom * frustumBottom; |
| float topSquared = frustumTop * frustumTop; |
| |
| float inverseLength = FastMath.invSqrt(nearSquared + leftSquared); |
| coeffLeft[0] = frustumNear * inverseLength; |
| coeffLeft[1] = -frustumLeft * inverseLength; |
| |
| inverseLength = FastMath.invSqrt(nearSquared + rightSquared); |
| coeffRight[0] = -frustumNear * inverseLength; |
| coeffRight[1] = frustumRight * inverseLength; |
| |
| inverseLength = FastMath.invSqrt(nearSquared + bottomSquared); |
| coeffBottom[0] = frustumNear * inverseLength; |
| coeffBottom[1] = -frustumBottom * inverseLength; |
| |
| inverseLength = FastMath.invSqrt(nearSquared + topSquared); |
| coeffTop[0] = -frustumNear * inverseLength; |
| coeffTop[1] = frustumTop * inverseLength; |
| } else { |
| coeffLeft[0] = 1; |
| coeffLeft[1] = 0; |
| |
| coeffRight[0] = -1; |
| coeffRight[1] = 0; |
| |
| coeffBottom[0] = 1; |
| coeffBottom[1] = 0; |
| |
| coeffTop[0] = -1; |
| coeffTop[1] = 0; |
| } |
| |
| projectionMatrix.fromFrustum(frustumNear, frustumFar, frustumLeft, frustumRight, frustumTop, frustumBottom, parallelProjection); |
| // projectionMatrix.transposeLocal(); |
| |
| // The frame is effected by the frustum values |
| // update it as well |
| onFrameChange(); |
| } |
| |
| /** |
| * <code>onFrameChange</code> updates the view frame of the camera. |
| */ |
| public void onFrameChange() { |
| TempVars vars = TempVars.get(); |
| |
| Vector3f left = getLeft(vars.vect1); |
| Vector3f direction = getDirection(vars.vect2); |
| Vector3f up = getUp(vars.vect3); |
| |
| float dirDotLocation = direction.dot(location); |
| |
| // left plane |
| Vector3f leftPlaneNormal = worldPlane[LEFT_PLANE].getNormal(); |
| leftPlaneNormal.x = left.x * coeffLeft[0]; |
| leftPlaneNormal.y = left.y * coeffLeft[0]; |
| leftPlaneNormal.z = left.z * coeffLeft[0]; |
| leftPlaneNormal.addLocal(direction.x * coeffLeft[1], direction.y |
| * coeffLeft[1], direction.z * coeffLeft[1]); |
| worldPlane[LEFT_PLANE].setConstant(location.dot(leftPlaneNormal)); |
| |
| // right plane |
| Vector3f rightPlaneNormal = worldPlane[RIGHT_PLANE].getNormal(); |
| rightPlaneNormal.x = left.x * coeffRight[0]; |
| rightPlaneNormal.y = left.y * coeffRight[0]; |
| rightPlaneNormal.z = left.z * coeffRight[0]; |
| rightPlaneNormal.addLocal(direction.x * coeffRight[1], direction.y |
| * coeffRight[1], direction.z * coeffRight[1]); |
| worldPlane[RIGHT_PLANE].setConstant(location.dot(rightPlaneNormal)); |
| |
| // bottom plane |
| Vector3f bottomPlaneNormal = worldPlane[BOTTOM_PLANE].getNormal(); |
| bottomPlaneNormal.x = up.x * coeffBottom[0]; |
| bottomPlaneNormal.y = up.y * coeffBottom[0]; |
| bottomPlaneNormal.z = up.z * coeffBottom[0]; |
| bottomPlaneNormal.addLocal(direction.x * coeffBottom[1], direction.y |
| * coeffBottom[1], direction.z * coeffBottom[1]); |
| worldPlane[BOTTOM_PLANE].setConstant(location.dot(bottomPlaneNormal)); |
| |
| // top plane |
| Vector3f topPlaneNormal = worldPlane[TOP_PLANE].getNormal(); |
| topPlaneNormal.x = up.x * coeffTop[0]; |
| topPlaneNormal.y = up.y * coeffTop[0]; |
| topPlaneNormal.z = up.z * coeffTop[0]; |
| topPlaneNormal.addLocal(direction.x * coeffTop[1], direction.y |
| * coeffTop[1], direction.z * coeffTop[1]); |
| worldPlane[TOP_PLANE].setConstant(location.dot(topPlaneNormal)); |
| |
| if (isParallelProjection()) { |
| worldPlane[LEFT_PLANE].setConstant(worldPlane[LEFT_PLANE].getConstant() + frustumLeft); |
| worldPlane[RIGHT_PLANE].setConstant(worldPlane[RIGHT_PLANE].getConstant() - frustumRight); |
| worldPlane[TOP_PLANE].setConstant(worldPlane[TOP_PLANE].getConstant() - frustumTop); |
| worldPlane[BOTTOM_PLANE].setConstant(worldPlane[BOTTOM_PLANE].getConstant() + frustumBottom); |
| } |
| |
| // far plane |
| worldPlane[FAR_PLANE].setNormal(left); |
| worldPlane[FAR_PLANE].setNormal(-direction.x, -direction.y, -direction.z); |
| worldPlane[FAR_PLANE].setConstant(-(dirDotLocation + frustumFar)); |
| |
| // near plane |
| worldPlane[NEAR_PLANE].setNormal(direction.x, direction.y, direction.z); |
| worldPlane[NEAR_PLANE].setConstant(dirDotLocation + frustumNear); |
| |
| viewMatrix.fromFrame(location, direction, up, left); |
| |
| vars.release(); |
| |
| // viewMatrix.transposeLocal(); |
| updateViewProjection(); |
| } |
| |
| /** |
| * @return true if parallel projection is enable, false if in normal perspective mode |
| * @see #setParallelProjection(boolean) |
| */ |
| public boolean isParallelProjection() { |
| return this.parallelProjection; |
| } |
| |
| /** |
| * Enable/disable parallel projection. |
| * |
| * @param value true to set up this camera for parallel projection is enable, false to enter normal perspective mode |
| */ |
| public void setParallelProjection(final boolean value) { |
| this.parallelProjection = value; |
| onFrustumChange(); |
| } |
| |
| /** |
| * @see Camera#getWorldCoordinates |
| */ |
| public Vector3f getWorldCoordinates(Vector2f screenPos, float zPos) { |
| return getWorldCoordinates(screenPos, zPos, null); |
| } |
| |
| /** |
| * @see Camera#getWorldCoordinates |
| */ |
| public Vector3f getWorldCoordinates(Vector2f screenPosition, |
| float zPos, Vector3f store) { |
| if (store == null) { |
| store = new Vector3f(); |
| } |
| |
| Matrix4f inverseMat = new Matrix4f(viewProjectionMatrix); |
| inverseMat.invertLocal(); |
| |
| store.set( |
| (screenPosition.x / getWidth() - viewPortLeft) / (viewPortRight - viewPortLeft) * 2 - 1, |
| (screenPosition.y / getHeight() - viewPortBottom) / (viewPortTop - viewPortBottom) * 2 - 1, |
| zPos * 2 - 1); |
| |
| float w = inverseMat.multProj(store, store); |
| store.multLocal(1f / w); |
| |
| return store; |
| } |
| |
| /** |
| * Converts the given position from world space to screen space. |
| * |
| * @see Camera#getScreenCoordinates |
| */ |
| public Vector3f getScreenCoordinates(Vector3f worldPos) { |
| return getScreenCoordinates(worldPos, null); |
| } |
| |
| /** |
| * Converts the given position from world space to screen space. |
| * |
| * @see Camera#getScreenCoordinates(Vector3f, Vector3f) |
| */ |
| public Vector3f getScreenCoordinates(Vector3f worldPosition, Vector3f store) { |
| if (store == null) { |
| store = new Vector3f(); |
| } |
| |
| // TempVars vars = vars.lock(); |
| // Quaternion tmp_quat = vars.quat1; |
| // tmp_quat.set( worldPosition.x, worldPosition.y, worldPosition.z, 1 ); |
| // viewProjectionMatrix.mult(tmp_quat, tmp_quat); |
| // tmp_quat.multLocal( 1.0f / tmp_quat.getW() ); |
| // store.x = ( ( tmp_quat.getX() + 1 ) * ( viewPortRight - viewPortLeft ) / 2 + viewPortLeft ) * getWidth(); |
| // store.y = ( ( tmp_quat.getY() + 1 ) * ( viewPortTop - viewPortBottom ) / 2 + viewPortBottom ) * getHeight(); |
| // store.z = ( tmp_quat.getZ() + 1 ) / 2; |
| // vars.release(); |
| |
| float w = viewProjectionMatrix.multProj(worldPosition, store); |
| store.divideLocal(w); |
| |
| store.x = ((store.x + 1f) * (viewPortRight - viewPortLeft) / 2f + viewPortLeft) * getWidth(); |
| store.y = ((store.y + 1f) * (viewPortTop - viewPortBottom) / 2f + viewPortBottom) * getHeight(); |
| store.z = (store.z + 1f) / 2f; |
| |
| return store; |
| } |
| |
| /** |
| * @return the width/resolution of the display. |
| */ |
| public int getWidth() { |
| return width; |
| } |
| |
| /** |
| * @return the height/resolution of the display. |
| */ |
| public int getHeight() { |
| return height; |
| } |
| |
| @Override |
| public String toString() { |
| return "Camera[location=" + location + "\n, direction=" + getDirection() + "\n" |
| + "res=" + width + "x" + height + ", parallel=" + parallelProjection + "\n" |
| + "near=" + frustumNear + ", far=" + frustumFar + "]"; |
| } |
| |
| public void write(JmeExporter e) throws IOException { |
| OutputCapsule capsule = e.getCapsule(this); |
| capsule.write(location, "location", Vector3f.ZERO); |
| capsule.write(rotation, "rotation", Quaternion.DIRECTION_Z); |
| capsule.write(frustumNear, "frustumNear", 1); |
| capsule.write(frustumFar, "frustumFar", 2); |
| capsule.write(frustumLeft, "frustumLeft", -0.5f); |
| capsule.write(frustumRight, "frustumRight", 0.5f); |
| capsule.write(frustumTop, "frustumTop", 0.5f); |
| capsule.write(frustumBottom, "frustumBottom", -0.5f); |
| capsule.write(coeffLeft, "coeffLeft", new float[2]); |
| capsule.write(coeffRight, "coeffRight", new float[2]); |
| capsule.write(coeffBottom, "coeffBottom", new float[2]); |
| capsule.write(coeffTop, "coeffTop", new float[2]); |
| capsule.write(viewPortLeft, "viewPortLeft", 0); |
| capsule.write(viewPortRight, "viewPortRight", 1); |
| capsule.write(viewPortTop, "viewPortTop", 1); |
| capsule.write(viewPortBottom, "viewPortBottom", 0); |
| capsule.write(width, "width", 0); |
| capsule.write(height, "height", 0); |
| capsule.write(name, "name", null); |
| } |
| |
| public void read(JmeImporter e) throws IOException { |
| InputCapsule capsule = e.getCapsule(this); |
| location = (Vector3f) capsule.readSavable("location", Vector3f.ZERO.clone()); |
| rotation = (Quaternion) capsule.readSavable("rotation", Quaternion.DIRECTION_Z.clone()); |
| frustumNear = capsule.readFloat("frustumNear", 1); |
| frustumFar = capsule.readFloat("frustumFar", 2); |
| frustumLeft = capsule.readFloat("frustumLeft", -0.5f); |
| frustumRight = capsule.readFloat("frustumRight", 0.5f); |
| frustumTop = capsule.readFloat("frustumTop", 0.5f); |
| frustumBottom = capsule.readFloat("frustumBottom", -0.5f); |
| coeffLeft = capsule.readFloatArray("coeffLeft", new float[2]); |
| coeffRight = capsule.readFloatArray("coeffRight", new float[2]); |
| coeffBottom = capsule.readFloatArray("coeffBottom", new float[2]); |
| coeffTop = capsule.readFloatArray("coeffTop", new float[2]); |
| viewPortLeft = capsule.readFloat("viewPortLeft", 0); |
| viewPortRight = capsule.readFloat("viewPortRight", 1); |
| viewPortTop = capsule.readFloat("viewPortTop", 1); |
| viewPortBottom = capsule.readFloat("viewPortBottom", 0); |
| width = capsule.readInt("width", 1); |
| height = capsule.readInt("height", 1); |
| name = capsule.readString("name", null); |
| onFrustumChange(); |
| onViewPortChange(); |
| onFrameChange(); |
| } |
| } |