diff --git a/jme3-core/src/main/java/com/jme3/math/Frustum.java b/jme3-core/src/main/java/com/jme3/math/Frustum.java new file mode 100644 index 0000000000..c2f5b326d7 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/math/Frustum.java @@ -0,0 +1,231 @@ +package com.jme3.math; + +public class Frustum { + + /** + * LEFT_PLANE represents the left plane of the camera frustum. + */ + public static final int LEFT_PLANE = 0; + /** + * RIGHT_PLANE represents the right plane of the camera frustum. + */ + public static final int RIGHT_PLANE = 1; + /** + * BOTTOM_PLANE represents the bottom plane of the camera frustum. + */ + public static final int BOTTOM_PLANE = 2; + /** + * TOP_PLANE represents the top plane of the camera frustum. + */ + public static final int TOP_PLANE = 3; + /** + * FAR_PLANE represents the far plane of the camera frustum. + */ + public static final int FAR_PLANE = 4; + /** + * NEAR_PLANE represents the near plane of the camera frustum. + */ + public static final int NEAR_PLANE = 5; + + /** + * FRUSTUM_PLANES represents the number of planes of the camera frustum. + */ + public static final int FRUSTUM_PLANES = 6; + + + /** + * MAX_WORLD_PLANES holds the maximum planes allowed by the system. + */ + public static final int MAX_WORLD_PLANES = 6; + + + + /** + * 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; + + //TODO this looks like a red flag + //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; + + public float[][] getCoeffFirstValue(){ + float[][] result = {coeffLeft, coeffRight, coeffBottom, coeffTop }; + return result; + } + + + + public Frustum( float frustumNear,float frustumFar,float frustumLeft,float frustumRight,float frustumTop,float frustumBottom){ + this.frustumNear = frustumNear; + this.frustumFar=frustumFar; + this.frustumLeft=frustumLeft; + this.frustumRight=frustumRight; + this.frustumTop=frustumTop; + this.frustumBottom=frustumBottom; + + coeffLeft = new float[2]; + coeffRight = new float[2]; + coeffBottom = new float[2]; + coeffTop = new float[2]; + } + + + public void resetTemporaryVariables(){ + coeffLeft = new float[2]; + coeffRight = new float[2]; + coeffBottom = new float[2]; + coeffTop = new float[2]; + } + + public void setTemporaryVariables(float[] left, float[] right, float[] bottom, float[] top){ + coeffLeft = left; + coeffRight = right; + coeffBottom = bottom; + coeffTop = top; + } + + public void copySettingFrom( Frustum modelFrustum){ + frustumNear = modelFrustum.frustumNear; + frustumFar = modelFrustum.frustumFar; + frustumLeft = modelFrustum.frustumLeft; + frustumRight = modelFrustum.frustumRight; + frustumTop = modelFrustum.frustumTop; + frustumBottom = modelFrustum.frustumBottom; + + coeffLeft[0] = modelFrustum.coeffLeft[0]; + coeffLeft[1] = modelFrustum.coeffLeft[1]; + coeffRight[0] = modelFrustum.coeffRight[0]; + coeffRight[1] = modelFrustum.coeffRight[1]; + coeffBottom[0] = modelFrustum.coeffBottom[0]; + coeffBottom[1] = modelFrustum.coeffBottom[1]; + coeffTop[0] = modelFrustum.coeffTop[0]; + coeffTop[1] = modelFrustum.coeffTop[1]; + } + + public void fixAspect( int width,int height){ + frustumRight = frustumTop * ((float) width / height); + frustumLeft = -frustumRight; + + } + + + public void setFrustum(float top, float bottom, float left, float right){ + this.frustumTop = top; + this.frustumBottom = bottom; + this.frustumLeft = left; + this.frustumRight = right; + } + + public void setFrustum(float top, float bottom, float left, float right,float near, float far){ + setFrustum(top, bottom, left,right); + this.frustumNear = near; + this.frustumFar = far; + } + + + //TODO try to remove these + public float getBottom(){ + return frustumBottom; + } + + public float getTop(){ + return frustumTop; + } + + public float getFar(){ + return frustumFar; + } + + public float getLeft(){ + return frustumLeft; + } + + public float getRight(){ + return frustumRight; + } + + public float getNear(){ + return frustumNear; + } + + + + public void setFar(float far){ + frustumFar = far; + } + + public void setNear(float near){ + frustumFar = near; + } + + + /** + * onFrustumChange 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(boolean parallelProjection) { + if (!parallelProjection) { + 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; + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/math/Matrixf.java b/jme3-core/src/main/java/com/jme3/math/Matrixf.java new file mode 100644 index 0000000000..8ab49058a6 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/math/Matrixf.java @@ -0,0 +1,687 @@ +/* + * Copyright (c) 2009-2012 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.math; + +import com.jme3.export.*; +import com.jme3.util.BufferUtils; +import com.jme3.util.TempVars; +import java.io.IOException; +import java.nio.FloatBuffer; +import java.util.logging.Logger; + +/** + * Matrix4f defines and maintains a 4x4 matrix in row major order. + * This matrix is intended for use in a translation and rotational capacity. + * It provides convenience methods for creating the matrix from a multitude + * of sources. + * + * Matrices are stored assuming column vectors on the right, with the translation + * in the rightmost column. Element numbering is row,column, so m03 is the zeroth + * row, third column, which is the "x" translation part. This means that the implicit + * storage order is column major. However, the get() and set() functions on float + * arrays default to row major order! + * + * @author Mark Powell + * @author Joshua Slack + */ +public abstract class Matrixf implements Savable, Cloneable, java.io.Serializable { + + static final long serialVersionUID = 1; + + protected static final Logger logger = Logger.getLogger(Matrix4f.class.getName()); + protected float[][] matrix; + protected int matrixSize; + + + public static final Matrix4f ZERO = new Matrix4f(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + public static final Matrix4f IDENTITY = new Matrix4f(); + + /** + * Constructor instantiates a new Matrix that is set to the + * identity matrix. + * + */ + public Matrixf() { + loadIdentity(); + } + + public abstract void loadIdentity(); + + /** + * constructs a matrix with the given values. + */ + public Matrixf(float[] values, int n){ + int c=0; + int line=0; + int i; + float[][] result = new float[n][n]; + while(ccopy transfers the contents of a given matrix to this + * matrix. If a null matrix is supplied, this matrix is set to the identity + * matrix. + * + * @param matrix + * the matrix to copy. + */ + public void copy(Matrixf matrix) { + if (null == matrix) { + loadIdentity(); + } else { + int i,j; + for(i=0;iget retrieves the values of this object into + * a float array in row-major order. + * + * @param matrix + * the matrix to set the values into. + */ + public void get(float[] matrix) { + get(matrix, true); + } + + /** + * set retrieves the values of this object into + * a float array. + * + * @param matrix + * the matrix to set the values into. + * @param rowMajor + * whether the outgoing data is in row or column major order. + */ + public void get(float[] matrix, boolean rowMajor) { + if (matrix.length != matrixSize*matrixSize) { + throw new IllegalArgumentException( + "Array must be of size "+ matrixSize*matrixSize); + } + + if (rowMajor) { + int i,j; + int current=0; + for(i=0;iget retrieves a value from the matrix at the given + * position. If the position is invalid a JmeException is + * thrown. + * + * @param i + * the row index. + * @param j + * the colum index. + * @return the value at (i, j). + */ + @SuppressWarnings("fallthrough") + public float get(int i, int j) { + + if(igetColumn returns one of three columns specified by the + * parameter. This column is returned as a float[4]. + * + * @param i + * the column to retrieve. Must be between 0 and 3. + * @param store + * the float array to store the result in. if null, a new one + * is created. + * @return the column specified by the index. + */ + public float[] getColumn(int i, float[] store) { + if (store == null) { + store = new float[matrixSize]; + } + + if(isetColumn sets a particular column of this matrix to that + * represented by the provided vector. + * + * @param i + * the column to set. + * @param column + * the data to set. + */ + public void setColumn(int i, float[] column) { + + if (column == null) { + logger.warning("Column is null. Ignoring."); + return; + } + + if(iset places a given value into the matrix at the given + * position. If the position is invalid a JmeException is + * thrown. + * + * @param i + * the row index. + * @param j + * the colum index. + * @param value + * the value for (i, j). + */ + @SuppressWarnings("fallthrough") + public void set(int i, int j, float value) { + + if(iset sets the values of this matrix from an array of + * values. + * + * @param matrix + * the matrix to set the value to. + * @throws JmeException + * if the array is not of size 16. + */ + public void set(float[][] matrix) { + if (matrix.length != matrixSize || matrix[0].length != matrixSize) { + throw new IllegalArgumentException( + "Array must be of size "+ matrixSize*matrixSize); + } + this.matrix = matrix; + } + + + + + /** + * set sets the values of this matrix from another matrix. + * + * @param matrix + * the matrix to read the value from. + */ + public Matrixf set(Matrixf matrix) { + this.matrix = matrix.getMatrix(); + return this; + } + + /** + * set sets the values of this matrix from an array of + * values assuming that the data is rowMajor order; + * + * @param matrix + * the matrix to set the value to. + */ + public void set(float[] matrix) { + set(matrix, true); + } + + /** + * set sets the values of this matrix from an array of + * values; + * + * @param matrix + * the matrix to set the value to. + * @param rowMajor + * whether the incoming data is in row or column major order. + */ + public void set(float[] matrix, boolean rowMajor) { + if (matrix.length != matrixSize*matrixSize) { + throw new IllegalArgumentException( + "Array must be of size "+matrixSize*matrixSize); + } + + if (rowMajor) { + set(matrix); + } else { + int i,j; + for(i=0;itranspose locally transposes this Matrix. + * + * @return this object for chaining. + */ + public Matrixf transposeLocal() { + + int i,j; + float tmp; + for(i=0;itoFloatBuffer returns a FloatBuffer object that contains + * the matrix data. + * + * @return matrix data as a FloatBuffer. + */ + public FloatBuffer toFloatBuffer() { + return toFloatBuffer(false); + } + + /** + * toFloatBuffer returns a FloatBuffer object that contains the + * matrix data. + * + * @param columnMajor + * if true, this buffer should be filled with column major data, + * otherwise it will be filled row major. + * @return matrix data as a FloatBuffer. The position is set to 0 for + * convenience. + */ + public FloatBuffer toFloatBuffer(boolean columnMajor) { + FloatBuffer fb = BufferUtils.createFloatBuffer(matrixSize*matrixSize); + fillFloatBuffer(fb, columnMajor); + fb.rewind(); + return fb; + } + + /** + * fillFloatBuffer fills a FloatBuffer object with + * the matrix data. + * @param fb the buffer to fill, must be correct size + * @return matrix data as a FloatBuffer. + */ + public FloatBuffer fillFloatBuffer(FloatBuffer fb) { + return fillFloatBuffer(fb, false); + } + + /** + * fillFloatBuffer fills a FloatBuffer object with the matrix + * data. + * + * @param fb + * the buffer to fill, starting at current position. Must have + * room for 16 more floats. + * @param columnMajor + * if true, this buffer should be filled with column major data, + * otherwise it will be filled row major. + * @return matrix data as a FloatBuffer. (position is advanced by 16 and any + * limit set is not changed). + */ + public FloatBuffer fillFloatBuffer(FloatBuffer fb, boolean columnMajor) { + + TempVars vars = TempVars.get(); + + + fillFloatArray(vars.matrixWrite, columnMajor); + fb.put(vars.matrixWrite, 0, matrixSize*matrixSize); + + vars.release(); + + return fb; + } + + public void fillFloatArray(float[] f, boolean columnMajor) { + if (columnMajor) { + int i,j; + int counter=0; + for(i=0;ireadFloatBuffer reads value for this matrix from a FloatBuffer. + * @param fb the buffer to read from, must be correct size + * @return this data as a FloatBuffer. + */ + public Matrixf readFloatBuffer(FloatBuffer fb) { + return readFloatBuffer(fb, false); + } + + /** + * readFloatBuffer reads value for this matrix from a FloatBuffer. + * @param fb the buffer to read from, must be correct size + * @param columnMajor if true, this buffer should be filled with column + * major data, otherwise it will be filled row major. + * @return this data as a FloatBuffer. + */ + public Matrixf readFloatBuffer(FloatBuffer fb, boolean columnMajor) { + + int i,j; + + + if (columnMajor) { + for(i=0;imult multiplies this matrix by a scalar. + * + * @param scalar + * the scalar to multiply this matrix by. + */ + public void multLocal(float scalar) { + int i,j; + for(i=0;imult multiplies this matrix with another matrix. The + * result matrix will then be returned. This matrix will be on the left hand + * side, while the parameter matrix will be on the right. + * + * @param in2 + * the matrix to multiply this matrix by. + * @param store + * where to store the result. It is safe for in2 and store to be + * the same object. + * @return the resultant matrix + */ + public Matrixf mult(Matrixf in2, Matrixf store) { + + int i,j,k; + for(i=0;imult multiplies this matrix with another matrix. The + * results are stored internally and a handle to this matrix will + * then be returned. This matrix will be on the left hand + * side, while the parameter matrix will be on the right. + * + * @param in2 + * the matrix to multiply this matrix by. + * @return the resultant matrix + */ + public Matrixf multLocal(Matrixf in2) { + return mult(in2, this); + } + + + + + + + + /** + * Inverts this matrix as a new Matrix4f. + * + * @return The new inverse matrix + */ + public Matrixf invert() { + return invert(null); + } + + public abstract Matrixf invert(Matrixf m); + + + + /** + * Returns a new matrix representing the adjoint of this matrix. + * + * @return The adjoint matrix + */ + public Matrixf adjoint() { + return adjoint(null); + } + + + /** + * Places the adjoint of this matrix in store (creates store if null.) + * + * @param store + * The matrix to store the result in. If null, a new matrix is created. + * @return store + */ + public abstract Matrixf adjoint(Matrixf store); + + + /** + * Sets all of the values in this matrix to zero. + * + * @return this matrix + */ + public Matrixf zero() { + int i,j; + for(i=0;iadd adds the values of a parameter matrix to this matrix. + * + * @param mat + * the matrix to add to this. + */ + public void addLocal(Matrixf mat) { + int i,j; + for(i=0;iCamera is a standalone, purely mathematical class for doing * camera-related computations. @@ -63,6 +45,10 @@ */ public class Camera implements Savable, Cloneable { + private Frustum frustum; + private DisplayViewPort displayViewPort; + private ProjectionMatrix projectionMatrix; + private static final Logger logger = Logger.getLogger(Camera.class.getName()); /** @@ -88,38 +74,8 @@ public enum FrustumIntersect { */ 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 */ @@ -128,57 +84,7 @@ public enum FrustumIntersect { * 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. */ @@ -195,11 +101,9 @@ public enum FrustumIntersect { * store the value for field parallelProjection */ private boolean parallelProjection = true; - protected Matrix4f projectionMatrixOverride = new Matrix4f(); + private boolean overrideProjection; - 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; @@ -208,8 +112,8 @@ public enum FrustumIntersect { * Serialization only. Do not use. */ public Camera() { - worldPlane = new Plane[MAX_WORLD_PLANES]; - for (int i = 0; i < MAX_WORLD_PLANES; i++) { + worldPlane = new Plane[Frustum.MAX_WORLD_PLANES]; + for (int i = 0; i < Frustum.MAX_WORLD_PLANES; i++) { worldPlane[i] = new Plane(); } } @@ -223,23 +127,10 @@ public Camera(int width, int height) { 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; - + frustum = new Frustum(1.0f,2.0f,-0.5f,0.5f,0.5f,-0.5f); + displayViewPort = new DisplayViewPort( 0.0f, 1.0f, 1.0f, 0.0f); + projectionMatrix = new ProjectionMatrix(); + this.width = width; this.height = height; @@ -257,26 +148,18 @@ public Camera clone() { cam.viewportChanged = true; cam.planeState = 0; - cam.worldPlane = new Plane[MAX_WORLD_PLANES]; + cam.worldPlane = new Plane[Frustum.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]; + //TODO I think this is redundant. is this a deep clone? + cam.getFrustum().resetTemporaryVariables(); 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.getTheProjectionMatrix().cloneFrom(projectionMatrix); cam.guiBounding = (BoundingBox) guiBounding.clone(); cam.update(); @@ -287,6 +170,20 @@ public Camera clone() { } } + + public Frustum getFrustum(){ + return frustum; + } + + public DisplayViewPort getDisplayViewPort(){ + return displayViewPort; + } + + public ProjectionMatrix getTheProjectionMatrix(){ + return projectionMatrix; + } + + /** * This method copies the settings of the given camera. * @@ -297,45 +194,24 @@ 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; + frustum.copySettingFrom(cam.getFrustum()); + displayViewPort.copySettingFrom(cam.getDisplayViewPort()); this.width = cam.width; this.height = cam.height; this.planeState = 0; this.viewportChanged = true; - for (int i = 0; i < MAX_WORLD_PLANES; ++i) { + for (int i = 0; i < Frustum.MAX_WORLD_PLANES; ++i) { worldPlane[i].setNormal(cam.worldPlane[i].getNormal()); worldPlane[i].setConstant(cam.worldPlane[i].getConstant()); } this.parallelProjection = cam.parallelProjection; this.overrideProjection = cam.overrideProjection; - this.projectionMatrixOverride.set(cam.projectionMatrixOverride); - this.viewMatrix.set(cam.viewMatrix); - this.projectionMatrix.set(cam.projectionMatrix); - this.viewProjectionMatrix.set(cam.viewProjectionMatrix); - + projectionMatrix.copySettingsFrom(cam.getTheProjectionMatrix()); + this.guiBounding.setXExtent(cam.guiBounding.getXExtent()); this.guiBounding.setYExtent(cam.guiBounding.getYExtent()); this.guiBounding.setZExtent(cam.guiBounding.getZExtent()); @@ -378,7 +254,8 @@ public String getName() { * @param clipPlane the plane * @param side the side the camera stands from the plane */ - public void setClipPlane(Plane clipPlane, Plane.Side side) { + public void setClipPlane(Plane clipPlane, Plane.Side side) { + //TODO move functionality to plane?? this-plane.dothisstuff float sideFactor = 1; if (side == Plane.Side.Negative) { sideFactor = -1; @@ -390,9 +267,8 @@ public void setClipPlane(Plane clipPlane, Plane.Side side) { TempVars vars = TempVars.get(); try { - Matrix4f p = projectionMatrixOverride.set(projectionMatrix); - - Matrix4f ivm = viewMatrix; + Matrix4f p = projectionMatrix.getSetMatrix(); + Matrix4f ivm = projectionMatrix.getViewMatrix(); Vector3f point = clipPlane.getNormal().mult(clipPlane.getConstant(), vars.vect1); Vector3f pp = ivm.mult(point, vars.vect2); @@ -456,8 +332,7 @@ public void resize(int width, int height, boolean fixAspect) { onViewPortChange(); if (fixAspect /*&& !parallelProjection*/) { - frustumRight = frustumTop * ((float) width / height); - frustumLeft = -frustumRight; + frustum.fixAspect(width, height); onFrustumChange(); } } @@ -468,20 +343,13 @@ public void resize(int width, int height, boolean fixAspect) { * * @return the value of the bottom frustum plane. */ + + //TODO this is SUPER weird and should be changed. check the calls made to this method public float getFrustumBottom() { - return frustumBottom; + return frustum.getBottom(); } - /** - * setFrustumBottom 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(); - } + /** * getFrustumFar gets the value of the far frustum plane. @@ -489,7 +357,7 @@ public void setFrustumBottom(float frustumBottom) { * @return the value of the far frustum plane. */ public float getFrustumFar() { - return frustumFar; + return frustum.getFar(); } /** @@ -498,7 +366,7 @@ public float getFrustumFar() { * @param frustumFar the value of the far frustum plane. */ public void setFrustumFar(float frustumFar) { - this.frustumFar = frustumFar; + frustum.setFar(frustumFar); onFrustumChange(); } @@ -508,26 +376,17 @@ public void setFrustumFar(float frustumFar) { * @return the value of the left frustum plane. */ public float getFrustumLeft() { - return frustumLeft; - } - - /** - * setFrustumLeft 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(); + return frustum.getLeft(); } + /** * getFrustumNear gets the value of the near frustum plane. * * @return the value of the near frustum plane. */ public float getFrustumNear() { - return frustumNear; + return frustum.getNear(); } /** @@ -536,7 +395,7 @@ public float getFrustumNear() { * @param frustumNear the value of the near frustum plane. */ public void setFrustumNear(float frustumNear) { - this.frustumNear = frustumNear; + frustum.setNear(frustumNear); onFrustumChange(); } @@ -546,18 +405,10 @@ public void setFrustumNear(float frustumNear) { * @return frustumRight the value of the right frustum plane. */ public float getFrustumRight() { - return frustumRight; + return frustum.getRight(); } - /** - * setFrustumRight 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(); - } + /** * getFrustumTop gets the value of the top frustum plane. @@ -565,18 +416,10 @@ public void setFrustumRight(float frustumRight) { * @return the value of the top frustum plane. */ public float getFrustumTop() { - return frustumTop; + return frustum.getTop(); } - /** - * setFrustumTop 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(); - } + /** * getLocation retrieves the location vector of the camera. @@ -745,12 +588,7 @@ public void normalize() { 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; + frustum.setFrustum(top, bottom, left, right, near, far); onFrustumChange(); } @@ -773,12 +611,7 @@ public void setFrustumPerspective(float fovY, float aspect, float near, 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; + frustum.setFrustum(h, -h, -w, w, near, far); // Camera is no longer parallel projection even if it was before parallelProjection = false; @@ -901,18 +734,10 @@ public void setPlaneState(int planeState) { * @return the left boundary of the viewport */ public float getViewPortLeft() { - return viewPortLeft; + return displayViewPort.getLeft(); } - /** - * setViewPortLeft sets the left boundary of the viewport - * - * @param left the left boundary of the viewport - */ - public void setViewPortLeft(float left) { - viewPortLeft = left; - onViewPortChange(); - } + /** * getViewPortRight gets the right boundary of the viewport @@ -920,37 +745,20 @@ public void setViewPortLeft(float left) { * @return the right boundary of the viewport */ public float getViewPortRight() { - return viewPortRight; - } - - /** - * setViewPortRight sets the right boundary of the viewport - * - * @param right the right boundary of the viewport - */ - public void setViewPortRight(float right) { - viewPortRight = right; - onViewPortChange(); + return displayViewPort.getRight(); } + /** * getViewPortTop gets the top boundary of the viewport * * @return the top boundary of the viewport */ public float getViewPortTop() { - return viewPortTop; + return displayViewPort.getTop(); } - /** - * setViewPortTop sets the top boundary of the viewport - * - * @param top the top boundary of the viewport - */ - public void setViewPortTop(float top) { - viewPortTop = top; - onViewPortChange(); - } + /** * getViewPortBottom gets the bottom boundary of the viewport @@ -958,19 +766,10 @@ public void setViewPortTop(float top) { * @return the bottom boundary of the viewport */ public float getViewPortBottom() { - return viewPortBottom; - } - - /** - * setViewPortBottom sets the bottom boundary of the viewport - * - * @param bottom the bottom boundary of the viewport - */ - public void setViewPortBottom(float bottom) { - viewPortBottom = bottom; - onViewPortChange(); + return displayViewPort.getBottom(); } + /** * setViewPort sets the boundaries of the viewport * @@ -980,10 +779,7 @@ public void setViewPortBottom(float bottom) { * @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; + displayViewPort.setDisplayViewPort(left, right, bottom, top); onViewPortChange(); } @@ -994,7 +790,7 @@ public void setViewPort(float left, float right, float bottom, float top) { * @return Distance from the far plane to the point. */ public float distanceToNearPlane(Vector3f pos) { - return worldPlane[NEAR_PLANE].pseudoDistance(pos); + return worldPlane[frustum.NEAR_PLANE].pseudoDistance(pos); } /** @@ -1024,11 +820,11 @@ public FrustumIntersect contains(BoundingVolume bound) { int mask; FrustumIntersect rVal = FrustumIntersect.Inside; - for (int planeCounter = FRUSTUM_PLANES; planeCounter >= 0; planeCounter--) { + for (int planeCounter = frustum.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 == frustum.FRUSTUM_PLANES) ? bound.getCheckPlane() : planeCounter; // int planeId = planeCounter; mask = 1 << (planeId); @@ -1079,7 +875,7 @@ public boolean containsGui(BoundingVolume bound) { * orientation of the camera. */ public Matrix4f getViewMatrix() { - return viewMatrix; + return projectionMatrix.getViewMatrix(); } /** @@ -1092,10 +888,10 @@ public Matrix4f getViewMatrix() { public void setProjectionMatrix(Matrix4f projMatrix) { if (projMatrix == null) { overrideProjection = false; - projectionMatrixOverride.loadIdentity(); + projectionMatrix.getProjectionMatrixOverride().loadIdentity(); } else { overrideProjection = true; - projectionMatrixOverride.set(projMatrix); + projectionMatrix.getProjectionMatrixOverride().set(projMatrix); } updateViewProjection(); } @@ -1108,22 +904,17 @@ public void setProjectionMatrix(Matrix4f projMatrix) { */ public Matrix4f getProjectionMatrix() { if (overrideProjection) { - return projectionMatrixOverride; + return projectionMatrix.getProjectionMatrixOverride(); } - return projectionMatrix; + return projectionMatrix.getProjectionMatrix(); } /** * Updates the view projection matrix. */ public void updateViewProjection() { - if (overrideProjection) { - viewProjectionMatrix.set(projectionMatrixOverride).multLocal(viewMatrix); - } else { - //viewProjectionMatrix.set(viewMatrix).multLocal(projectionMatrix); - viewProjectionMatrix.set(projectionMatrix).multLocal(viewMatrix); - } + projectionMatrix.updateViewProjection(overrideProjection); } /** @@ -1132,7 +923,7 @@ public void updateViewProjection() { * precomputed so as to not compute it every time an object is rendered. */ public Matrix4f getViewProjectionMatrix() { - return viewProjectionMatrix; + return projectionMatrix.getviewProjectionMatrix(); } /** @@ -1161,69 +952,11 @@ public void onViewPortChange() { } 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(sx + xExtent, sy + yExtent, 0); - guiBounding.setXExtent(xExtent); - guiBounding.setYExtent(yExtent); - guiBounding.setZExtent(Float.MAX_VALUE); - } - - /** - * onFrustumChange 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(); + displayViewPort.setGuiBounding(width, height, guiBounding); } + + /** * onFrameChange updates the view frame of the camera. */ @@ -1235,60 +968,61 @@ public void onFrameChange() { Vector3f up = getUp(vars.vect3); float dirDotLocation = direction.dot(location); - + float[][] coeff=frustum.getCoeffFirstValue(); + // 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)); + Vector3f leftPlaneNormal = worldPlane[frustum.LEFT_PLANE].getNormal(); + leftPlaneNormal.x = left.x * coeff[0][0]; + leftPlaneNormal.y = left.y * coeff[0][0]; + leftPlaneNormal.z = left.z * coeff[0][0]; + leftPlaneNormal.addLocal(direction.x * coeff[0][1], direction.y + * coeff[0][1], direction.z * coeff[0][1]); + worldPlane[frustum.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)); + Vector3f rightPlaneNormal = worldPlane[frustum.RIGHT_PLANE].getNormal(); + rightPlaneNormal.x = left.x * coeff[1][0]; + rightPlaneNormal.y = left.y * coeff[1][0]; + rightPlaneNormal.z = left.z * coeff[1][0]; + rightPlaneNormal.addLocal(direction.x * coeff[1][1], direction.y + * coeff[1][1], direction.z * coeff[1][1]); + worldPlane[frustum.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)); + Vector3f bottomPlaneNormal = worldPlane[frustum.BOTTOM_PLANE].getNormal(); + bottomPlaneNormal.x = up.x * coeff[2][0]; + bottomPlaneNormal.y = up.y * coeff[2][0]; + bottomPlaneNormal.z = up.z * coeff[2][0]; + bottomPlaneNormal.addLocal(direction.x * coeff[2][1], direction.y + * coeff[2][1], direction.z * coeff[2][1]); + worldPlane[frustum.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)); + Vector3f topPlaneNormal = worldPlane[frustum.TOP_PLANE].getNormal(); + topPlaneNormal.x = up.x * coeff[3][0]; + topPlaneNormal.y = up.y * coeff[3][0]; + topPlaneNormal.z = up.z * coeff[3][0]; + topPlaneNormal.addLocal(direction.x * coeff[3][1], direction.y + * coeff[3][1], direction.z * coeff[3][1]); + worldPlane[frustum.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); + worldPlane[frustum.LEFT_PLANE].setConstant(worldPlane[frustum.LEFT_PLANE].getConstant() + frustum.getLeft()); + worldPlane[frustum.RIGHT_PLANE].setConstant(worldPlane[frustum.RIGHT_PLANE].getConstant() - frustum.getRight()); + worldPlane[frustum.TOP_PLANE].setConstant(worldPlane[frustum.TOP_PLANE].getConstant() - frustum.getTop()); + worldPlane[frustum.BOTTOM_PLANE].setConstant(worldPlane[frustum.BOTTOM_PLANE].getConstant() + frustum.getBottom()); } // far plane - worldPlane[FAR_PLANE].setNormal(left); - worldPlane[FAR_PLANE].setNormal(-direction.x, -direction.y, -direction.z); - worldPlane[FAR_PLANE].setConstant(-(dirDotLocation + frustumFar)); + worldPlane[frustum.FAR_PLANE].setNormal(left); + worldPlane[frustum.FAR_PLANE].setNormal(-direction.x, -direction.y, -direction.z); + worldPlane[frustum.FAR_PLANE].setConstant(-(dirDotLocation + frustum.getFar())); // near plane - worldPlane[NEAR_PLANE].setNormal(direction.x, direction.y, direction.z); - worldPlane[NEAR_PLANE].setConstant(dirDotLocation + frustumNear); + worldPlane[frustum.NEAR_PLANE].setNormal(direction.x, direction.y, direction.z); + worldPlane[frustum.NEAR_PLANE].setConstant(dirDotLocation + frustum.getNear()); - viewMatrix.fromFrame(location, direction, up, left); + projectionMatrix.getViewMatrix().fromFrame(location, direction, up, left); vars.release(); @@ -1311,7 +1045,18 @@ public boolean isParallelProjection() { */ public void setParallelProjection(final boolean value) { this.parallelProjection = value; - onFrustumChange(); + onFrustumChange(); + } + + + public void onFrustumChange(){ + frustum.onFrustumChange(isParallelProjection()); + projectionMatrix.getProjectionMatrix().fromFrustum(frustum.getNear(), frustum.getFar(), frustum.getLeft(), frustum.getRight(), frustum.getTop(), frustum.getBottom(), parallelProjection); +// projectionMatrix.transposeLocal(); + + // The frame is effected by the frustum values + // update it as well + onFrameChange(); } /** @@ -1356,12 +1101,12 @@ public Vector3f getWorldCoordinates(Vector2f screenPosition, store = new Vector3f(); } - Matrix4f inverseMat = new Matrix4f(viewProjectionMatrix); + Matrix4f inverseMat = new Matrix4f(projectionMatrix.getviewProjectionMatrix()); inverseMat.invertLocal(); store.set( - (screenPosition.x / getWidth() - viewPortLeft) / (viewPortRight - viewPortLeft) * 2 - 1, - (screenPosition.y / getHeight() - viewPortBottom) / (viewPortTop - viewPortBottom) * 2 - 1, + (screenPosition.x / getWidth() - displayViewPort.getLeft()) / (displayViewPort.getRight() - displayViewPort.getLeft()) * 2 - 1, + (screenPosition.y / getHeight() - displayViewPort.getBottom()) / (displayViewPort.getTop() - displayViewPort.getBottom()) * 2 - 1, projectionZPos * 2 - 1); float w = inverseMat.multProj(store, store); @@ -1399,11 +1144,11 @@ public Vector3f getScreenCoordinates(Vector3f worldPosition, Vector3f store) { // store.z = ( tmp_quat.getZ() + 1 ) / 2; // vars.release(); - float w = viewProjectionMatrix.multProj(worldPosition, store); + float w = projectionMatrix.getviewProjectionMatrix().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.x = ((store.x + 1f) * displayViewPort.storeXValue()) * getWidth(); + store.y = ((store.y + 1f) * displayViewPort.storeYValue())* getHeight(); store.z = (store.z + 1f) / 2f; return store; @@ -1427,27 +1172,31 @@ public int getHeight() { public String toString() { return "Camera[location=" + location + "\n, direction=" + getDirection() + "\n" + "res=" + width + "x" + height + ", parallel=" + parallelProjection + "\n" - + "near=" + frustumNear + ", far=" + frustumFar + "]"; + + "near=" + frustum.getNear() + ", far=" + frustum.getFar() + "]"; } 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(frustum.getNear(), "frustumNear", 1); + capsule.write(frustum.getFar(), "frustumFar", 2); + capsule.write(frustum.getNear(), "frustumLeft", -0.5f); + capsule.write(frustum.getRight(), "frustumRight", 0.5f); + capsule.write(frustum.getTop(), "frustumTop", 0.5f); + capsule.write(frustum.getBottom(), "frustumBottom", -0.5f); + + float[][] coeff = frustum.getCoeffFirstValue(); + capsule.write(coeff[0], "coeffLeft", new float[2]); + capsule.write(coeff[1], "coeffRight", new float[2]); + capsule.write(coeff[2], "coeffBottom", new float[2]); + capsule.write(coeff[3], "coeffTop", new float[2]); + + float[] viewPorts = displayViewPort.getViewPorts(); + capsule.write(viewPorts[0], "viewPortLeft", 0); + capsule.write(viewPorts[1], "viewPortRight", 1); + capsule.write(viewPorts[2], "viewPortTop", 1); + capsule.write(viewPorts[3], "viewPortBottom", 0); capsule.write(width, "width", 0); capsule.write(height, "height", 0); capsule.write(name, "name", null); @@ -1457,20 +1206,27 @@ 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); + + frustum.setFrustum( + capsule.readFloat("frustumTop", 0.5f), + capsule.readFloat("frustumBottom", -0.5f), + capsule.readFloat("frustumLeft", -0.5f), + capsule.readFloat("frustumRight", 0.5f), + capsule.readFloat("frustumNear", 1), + capsule.readFloat("frustumFar", 2)); + + frustum.setTemporaryVariables( + capsule.readFloatArray("coeffLeft", new float[2]), + capsule.readFloatArray("coeffRight", new float[2]), + capsule.readFloatArray("coeffBottom", new float[2]), + capsule.readFloatArray("coeffTop", new float[2])); + + displayViewPort.setDisplayViewPort( + capsule.readFloat("viewPortLeft", 0), + capsule.readFloat("viewPortRight", 1), + capsule.readFloat("viewPortTop", 1), + capsule.readFloat("viewPortBottom", 0)); + width = capsule.readInt("width", 1); height = capsule.readInt("height", 1); name = capsule.readString("name", null); diff --git a/jme3-core/src/main/java/com/jme3/renderer/DisplayViewPort.java b/jme3-core/src/main/java/com/jme3/renderer/DisplayViewPort.java new file mode 100644 index 0000000000..f6c24c4f5b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/renderer/DisplayViewPort.java @@ -0,0 +1,112 @@ +package com.jme3.renderer; + +import com.jme3.bounding.BoundingBox; + +public class DisplayViewPort { + + + //view port coordinates + /** + * Percent value on display where horizontal viewing starts for this camera. + * Default is 0. + */ + private float viewPortLeft; + /** + * Percent value on display where horizontal viewing ends for this camera. + * Default is 1. + */ + private float viewPortRight; + /** + * Percent value on display where vertical viewing ends for this camera. + * Default is 1. + */ + private float viewPortTop; + /** + * Percent value on display where vertical viewing begins for this camera. + * Default is 0. + */ + private float viewPortBottom; + + public float getLeft(){ + return viewPortLeft; + } + + public float getRight(){ + return viewPortRight; + } + + public float getTop(){ + return viewPortTop; + } + + public float getBottom(){ + return viewPortBottom; + } + + + public void setLeft(float value){ + viewPortLeft=value; + } + + public void setRight(float value){ + viewPortRight=value; + } + + public void setTop(float value){ + viewPortTop =value; + } + + public void setBottom(float value){ + viewPortBottom=value; + } + + public DisplayViewPort(float viewPortLeft,float viewPortRight,float viewPortTop, float viewPortBottom){ + this.viewPortLeft = viewPortLeft; + this.viewPortRight = viewPortRight; + this.viewPortTop = viewPortTop; + this.viewPortBottom = viewPortBottom; + } + + public void setDisplayViewPort(float viewPortLeft,float viewPortRight,float viewPortTop, float viewPortBottom){ + this.viewPortLeft = viewPortLeft; + this.viewPortRight = viewPortRight; + this.viewPortTop = viewPortTop; + this.viewPortBottom = viewPortBottom; + } + + public void copySettingFrom(DisplayViewPort cloneModel){ + this.viewPortLeft = cloneModel.viewPortLeft; + this.viewPortRight = cloneModel.viewPortRight; + this.viewPortTop = cloneModel.viewPortTop; + this.viewPortBottom = cloneModel.viewPortBottom; + } + + public void setGuiBounding(int width, int height, BoundingBox guiBounding){ + 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(sx + xExtent, sy + yExtent, 0); + guiBounding.setXExtent(xExtent); + guiBounding.setYExtent(yExtent); + guiBounding.setZExtent(Float.MAX_VALUE); + } + + public float storeXValue(){ + return (viewPortRight - viewPortLeft) / 2f + viewPortLeft; + } + + public float storeYValue(){ + return (viewPortTop - viewPortBottom) / 2f + viewPortBottom; + } + + public float[] getViewPorts(){ + float[] result = {viewPortLeft,viewPortRight,viewPortTop,viewPortBottom}; + return result; + } + + + +} diff --git a/jme3-core/src/main/java/com/jme3/renderer/ProjectionMatrix.java b/jme3-core/src/main/java/com/jme3/renderer/ProjectionMatrix.java new file mode 100644 index 0000000000..cf27637c5c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/renderer/ProjectionMatrix.java @@ -0,0 +1,63 @@ +package com.jme3.renderer; + +import com.jme3.math.Matrix4f; + +public class ProjectionMatrix { + + protected Matrix4f projectionMatrixOverride = new Matrix4f(); + protected Matrix4f viewMatrix = new Matrix4f(); + protected Matrix4f projectionMatrix = new Matrix4f(); + protected Matrix4f viewProjectionMatrix = new Matrix4f(); + + public Matrix4f getviewProjectionMatrix(){ + return viewProjectionMatrix; + } + + public void cloneFrom(ProjectionMatrix original){ + if (projectionMatrixOverride != null) { + this.projectionMatrixOverride = (Matrix4f) original.projectionMatrixOverride.clone(); + } + this.viewMatrix = (Matrix4f) original.viewMatrix.clone(); + this.projectionMatrix = (Matrix4f) original.projectionMatrix.clone(); + this.viewProjectionMatrix = (Matrix4f) original.viewProjectionMatrix.clone(); + + } + + + public void copySettingsFrom(ProjectionMatrix theProjectionMatrix) { + this.projectionMatrixOverride.set(theProjectionMatrix.projectionMatrixOverride); + + this.viewMatrix.set(theProjectionMatrix.viewMatrix); + this.projectionMatrix.set(theProjectionMatrix.projectionMatrix); + this.viewProjectionMatrix.set(theProjectionMatrix.viewProjectionMatrix); + + } + + public Matrix4f getViewMatrix(){ + return viewMatrix; + } + + public Matrix4f getSetMatrix(){ + return (Matrix4f) projectionMatrixOverride.set(projectionMatrix); + } + + public Matrix4f getProjectionMatrixOverride(){ + return projectionMatrixOverride; + } + + public Matrix4f getProjectionMatrix(){ + return projectionMatrix; + } + + public void updateViewProjection(boolean overrideProjection) { + if (overrideProjection) { + viewProjectionMatrix.set(projectionMatrixOverride).multLocal(viewMatrix); + } else { + //viewProjectionMatrix.set(viewMatrix).multLocal(projectionMatrix); + viewProjectionMatrix.set(projectionMatrix).multLocal(viewMatrix); + } + + } + + +} diff --git a/jme3-core/src/test/java/com/jme3/math/Matrix4fTest.java b/jme3-core/src/test/java/com/jme3/math/Matrix4fTest.java new file mode 100644 index 0000000000..2d9c86b04c --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/math/Matrix4fTest.java @@ -0,0 +1,399 @@ +package com.jme3.math; + +import static org.junit.Assert.*; + +import java.nio.FloatBuffer; + +import org.junit.Test; + +import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingSphere; +import com.jme3.bounding.BoundingVolume; +import com.jme3.bounding.BoundingVolume.Type; +import com.jme3.collision.Collidable; +import com.jme3.collision.CollisionResults; +import com.jme3.collision.UnsupportedCollisionException; +import com.jme3.math.FastMath; +import com.jme3.math.Matrix4f; +import com.jme3.math.Plane; +import com.jme3.math.Plane.Side; +import com.jme3.math.Quaternion; +import com.jme3.math.Ray; +import com.jme3.math.Transform; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.renderer.queue.GeometryList; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.util.BufferUtils; +import com.jme3.util.TempVars; +import com.sun.accessibility.internal.resources.accessibility; +import com.sun.javafx.geom.Vec3f; + +import javafx.beans.property.FloatProperty; +import sun.net.www.content.audio.x_aiff; + +public class Matrix4fTest { + float[] numbers = {-1.0f,0.0f,-0.0f,0.0f,0.0f,1.0f,-0.0f,0.0f,0.0f,-0.0f,-1.0f,0.0f,-0.0f,-0.0f,0.0f,1.0f}; + Matrix4f matrix = new Matrix4f(numbers); + + @Test + public void testCopy() + { + float[] numbers2 = {3.0f,0.0f,-0.0f,0.0f,0.0f,1.0f,-0.0f,0.0f,0.0f,-0.0f,-1.0f,0.0f,-0.0f,-0.0f,0.0f,1.0f}; + Matrix4f newMatrix = new Matrix4f(numbers2); + newMatrix.copy(matrix); + assertTrue(matrix.equals(newMatrix)); + } + + @Test + public void testFromFrame() + { + Vector3f location = new Vector3f(1.0f, 2.0f, 3.0f); + Vector3f direction = new Vector3f(2.0f, 3.0f, 4.0f); + Vector3f up = new Vector3f(1.0f, 2.0f, 4.0f); + Vector3f left = new Vector3f(5.0f, 0.0f, 1.0f); + matrix.fromFrame(location, direction, up, left); + TempVars vars = TempVars.get(); + assertTrue(vars.vect1.equals(new Vector3f(2.0f,3.0f,4.0f))); + assertTrue(vars.vect2.equals(new Vector3f(4.0f,-4.0f,1.0f))); + assertTrue(vars.vect3.equals(new Vector3f(-19.0f,-14.0f,20.0f))); + //System.out.println(vars.vect3); + } + + @Test + public void testGetMatrix() + { + float[] matrixFloat = {-10.0f,0.0f,-0.0f,0.0f,0.0f,1.0f,-0.0f,0.0f,0.0f,-0.0f,-1.0f,0.0f,-0.0f,-0.0f,0.0f,1.0f}; + matrix.get(matrixFloat); + for (int i=0; iC, B, A, C, B, A, C, B, A, it should result in + *
A, A, A, B, B, B, C, C, C
. + * + * @param materials The pre-sorted list of materials to check sorting for. + */ + private void testSort(Material ... materials) { + GeometryList gl = new GeometryList(comparator); + for (int g = 0; g < 5; g++) { + for (int i = materials.length - 1; i >= 0; i--) { + Geometry geom = new Geometry("geom", mesh); + Material clonedMaterial = materials[i].clone(); + + if (materials[i].getActiveTechnique() != null) { + String techniqueName = materials[i].getActiveTechnique().getDef().getName(); + clonedMaterial.selectTechnique(techniqueName, renderManager); + } else { + clonedMaterial.selectTechnique("Default", renderManager); + } + + geom.setMaterial(clonedMaterial); + gl.add(geom); + } + } + gl.sort(); + + for (int i = 0; i < gl.size(); i++) { + Material mat = gl.get(i).getMaterial(); + String sortId = Integer.toHexString(mat.getSortId()).toUpperCase(); + System.out.print(sortId + "\t"); + System.out.println(mat); + } + + Set alreadySeen = new HashSet(); + Material current = null; + for (int i = 0; i < gl.size(); i++) { + Material mat = gl.get(i).getMaterial(); + if (current == null) { + current = mat; + } else if (!current.getName().equals(mat.getName())) { + assert !alreadySeen.contains(mat.getName()); + alreadySeen.add(current.getName()); + current = mat; + } + } + + for (int i = 0; i < materials.length; i++) { + for (int g = 0; g < 5; g++) { + int index = i * 5 + g; + Material mat1 = gl.get(index).getMaterial(); + Material mat2 = materials[i]; + assert mat1.getName().equals(mat2.getName()) : + mat1.getName() + " != " + mat2.getName() + " for index " + index; + } + } + } + + @Test + public void testSortByMaterialDef() { + Material lightingMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material particleMat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md"); + Material unshadedMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + Material skyMat = new Material(assetManager, "Common/MatDefs/Misc/Sky.j3md"); + + lightingMat.setName("MatLight"); + particleMat.setName("MatParticle"); + unshadedMat.setName("MatUnshaded"); + skyMat.setName("MatSky"); + testSort(skyMat, lightingMat, particleMat, unshadedMat); + } + + @Test + public void testSortByTechnique() { + Material lightingMatDefault = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material lightingPreShadow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material lightingPostShadow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material lightingMatPreNormalPass = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material lightingMatGBuf = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material lightingMatGlow = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + + lightingMatDefault.setName("TechDefault"); + lightingMatDefault.selectTechnique("Default", renderManager); + + lightingPostShadow.setName("TechPostShad"); + lightingPostShadow.selectTechnique("PostShadow", renderManager); + + lightingPreShadow.setName("TechPreShad"); + lightingPreShadow.selectTechnique("PreShadow", renderManager); + + lightingMatPreNormalPass.setName("TechNorm"); + lightingMatPreNormalPass.selectTechnique("PreNormalPass", renderManager); + + lightingMatGBuf.setName("TechGBuf"); + lightingMatGBuf.selectTechnique("GBuf", renderManager); + + lightingMatGlow.setName("TechGlow"); + lightingMatGlow.selectTechnique("Glow", renderManager); + + testSort(lightingMatGlow, lightingPreShadow, lightingMatPreNormalPass, + lightingMatDefault, lightingPostShadow, lightingMatGBuf); + } + + @Test(expected = AssertionError.class) + public void testNoSortByParam() { + Material sameMat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + Material sameMat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + + sameMat1.setName("MatRed"); + sameMat1.setColor("Color", ColorRGBA.Red); + + sameMat2.setName("MatBlue"); + sameMat2.setColor("Color", ColorRGBA.Blue); + + testSort(sameMat1, sameMat2); + } + + private Texture createTexture(String name) { + ByteBuffer bb = BufferUtils.createByteBuffer(3); + Image image = new Image(Format.RGB8, 1, 1, bb, ColorSpace.sRGB); + Texture2D texture = new Texture2D(image); + texture.setName(name); + return texture; + } + + @Test + public void testSortByTexture() { + Material texture1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + Material texture2Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + Material texture3Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + + Texture tex1 = createTexture("A"); + tex1.getImage().setId(1); + + Texture tex2 = createTexture("B"); + tex2.getImage().setId(2); + + Texture tex3 = createTexture("C"); + tex3.getImage().setId(3); + + texture1Mat.setName("TexA"); + texture1Mat.setTexture("ColorMap", tex1); + + texture2Mat.setName("TexB"); + texture2Mat.setTexture("ColorMap", tex2); + + texture3Mat.setName("TexC"); + texture3Mat.setTexture("ColorMap", tex3); + + testSort(texture1Mat, texture2Mat, texture3Mat); + } + + @Test + public void testSortByShaderDefines() { + Material lightingMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material lightingMatVColor = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material lightingMatVLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material lightingMatTC = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material lightingMatVColorLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material lightingMatTCVColorLight = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + + lightingMat.setName("DefNone"); + + lightingMatVColor.setName("DefVC"); + lightingMatVColor.setBoolean("UseVertexColor", true); + + lightingMatVLight.setName("DefVL"); + lightingMatVLight.setBoolean("VertexLighting", true); + + lightingMatTC.setName("DefTC"); + lightingMatTC.setBoolean("SeparateTexCoord", true); + + lightingMatVColorLight.setName("DefVCVL"); + lightingMatVColorLight.setBoolean("UseVertexColor", true); + lightingMatVColorLight.setBoolean("VertexLighting", true); + + lightingMatTCVColorLight.setName("DefVCVLTC"); + lightingMatTCVColorLight.setBoolean("UseVertexColor", true); + lightingMatTCVColorLight.setBoolean("VertexLighting", true); + lightingMatTCVColorLight.setBoolean("SeparateTexCoord", true); + + testSort(lightingMat, lightingMatVColor, lightingMatVLight, + lightingMatVColorLight, lightingMatTC, lightingMatTCVColorLight); + } + + @Test + public void testSortByAll() { + Material matBase1 = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + Material matBase2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + + Texture texBase = createTexture("BASE"); + texBase.getImage().setId(1); + Texture tex1 = createTexture("1"); + tex1.getImage().setId(2); + Texture tex2 = createTexture("2"); + tex2.getImage().setId(3); + + matBase1.setName("BASE"); + matBase1.selectTechnique("Default", renderManager); + matBase1.setBoolean("UseVertexColor", true); + matBase1.setTexture("DiffuseMap", texBase); + + Material mat1100 = matBase1.clone(); + mat1100.setName("1100"); + mat1100.selectTechnique("PreShadow", renderManager); + + Material mat1101 = matBase1.clone(); + mat1101.setName("1101"); + mat1101.selectTechnique("PreShadow", renderManager); + mat1101.setTexture("DiffuseMap", tex1); + + Material mat1102 = matBase1.clone(); + mat1102.setName("1102"); + mat1102.selectTechnique("PreShadow", renderManager); + mat1102.setTexture("DiffuseMap", tex2); + + Material mat1110 = matBase1.clone(); + mat1110.setName("1110"); + mat1110.selectTechnique("PreShadow", renderManager); + mat1110.setFloat("AlphaDiscardThreshold", 2f); + + Material mat1120 = matBase1.clone(); + mat1120.setName("1120"); + mat1120.selectTechnique("PreShadow", renderManager); + mat1120.setBoolean("UseInstancing", true); + + Material mat1121 = matBase1.clone(); + mat1121.setName("1121"); + mat1121.selectTechnique("PreShadow", renderManager); + mat1121.setBoolean("UseInstancing", true); + mat1121.setTexture("DiffuseMap", tex1); + + Material mat1122 = matBase1.clone(); + mat1122.setName("1122"); + mat1122.selectTechnique("PreShadow", renderManager); + mat1122.setBoolean("UseInstancing", true); + mat1122.setTexture("DiffuseMap", tex2); + + Material mat1140 = matBase1.clone(); + mat1140.setName("1140"); + mat1140.selectTechnique("PreShadow", renderManager); + mat1140.setFloat("AlphaDiscardThreshold", 2f); + mat1140.setBoolean("UseInstancing", true); + + Material mat1200 = matBase1.clone(); + mat1200.setName("1200"); + mat1200.selectTechnique("PostShadow", renderManager); + + Material mat1210 = matBase1.clone(); + mat1210.setName("1210"); + mat1210.selectTechnique("PostShadow", renderManager); + mat1210.setFloat("AlphaDiscardThreshold", 2f); + + Material mat1220 = matBase1.clone(); + mat1220.setName("1220"); + mat1220.selectTechnique("PostShadow", renderManager); + mat1220.setBoolean("UseInstancing", true); + + Material mat2000 = matBase2.clone(); + mat2000.setName("2000"); + + testSort(mat1100, mat1101, mat1102, mat1110, + mat1120, mat1121, mat1122, mat1140, + mat1200, mat1210, mat1220, mat2000); + } +}