aboutsummaryrefslogtreecommitdiff
path: root/engine/src/core/com/jme3/renderer
diff options
context:
space:
mode:
authorScott Barta <sbarta@google.com>2012-03-01 12:35:35 -0800
committerScott Barta <sbarta@google.com>2012-03-01 12:40:08 -0800
commit59b2e6871c65f58fdad78cd7229c292f6a177578 (patch)
tree2d4e7bfc05b93f40b34675d77e403dd1c25efafd /engine/src/core/com/jme3/renderer
parentf9b30489e75ac1eabc365064959804e99534f7ab (diff)
downloadjmonkeyengine-59b2e6871c65f58fdad78cd7229c292f6a177578.tar.gz
Adds the jMonkeyEngine library to the build.
Adds the jMonkeyEngine open source 3D game engine to the build. This is built as a static library and is only used by the Finsky client. Change-Id: I06a3f054df7b8a67757267d884854f70c5a16ca0
Diffstat (limited to 'engine/src/core/com/jme3/renderer')
-rw-r--r--engine/src/core/com/jme3/renderer/Camera.java1436
-rw-r--r--engine/src/core/com/jme3/renderer/Caps.java360
-rw-r--r--engine/src/core/com/jme3/renderer/GL1Renderer.java26
-rw-r--r--engine/src/core/com/jme3/renderer/IDList.java121
-rw-r--r--engine/src/core/com/jme3/renderer/RenderContext.java319
-rw-r--r--engine/src/core/com/jme3/renderer/RenderManager.java1170
-rw-r--r--engine/src/core/com/jme3/renderer/Renderer.java305
-rw-r--r--engine/src/core/com/jme3/renderer/RendererException.java49
-rw-r--r--engine/src/core/com/jme3/renderer/Statistics.java255
-rw-r--r--engine/src/core/com/jme3/renderer/ViewPort.java363
-rw-r--r--engine/src/core/com/jme3/renderer/package.html38
-rw-r--r--engine/src/core/com/jme3/renderer/queue/GeometryComparator.java53
-rw-r--r--engine/src/core/com/jme3/renderer/queue/GeometryList.java143
-rw-r--r--engine/src/core/com/jme3/renderer/queue/GuiComparator.java60
-rw-r--r--engine/src/core/com/jme3/renderer/queue/NullComparator.java51
-rw-r--r--engine/src/core/com/jme3/renderer/queue/OpaqueComparator.java96
-rw-r--r--engine/src/core/com/jme3/renderer/queue/RenderQueue.java377
-rw-r--r--engine/src/core/com/jme3/renderer/queue/TransparentComparator.java102
18 files changed, 5324 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/renderer/Camera.java b/engine/src/core/com/jme3/renderer/Camera.java
new file mode 100644
index 0000000..74ba6cf
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/Camera.java
@@ -0,0 +1,1436 @@
+/*
+ * 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();
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/Caps.java b/engine/src/core/com/jme3/renderer/Caps.java
new file mode 100644
index 0000000..4dd1ed8
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/Caps.java
@@ -0,0 +1,360 @@
+/*
+ * 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.renderer;
+
+import com.jme3.shader.Shader;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.FrameBuffer.RenderBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+import java.util.Collection;
+
+/**
+ * <code>Caps</code> is an enum specifying a capability that the {@link Renderer}
+ * supports.
+ *
+ * @author Kirill Vainer
+ */
+public enum Caps {
+
+ /**
+ * Supports {@link FrameBuffer FrameBuffers}.
+ * <p>
+ * OpenGL: Renderer exposes the GL_EXT_framebuffer_object extension.<br>
+ * OpenGL ES: Renderer supports OpenGL ES 2.0.
+ */
+ FrameBuffer,
+
+ /**
+ * Supports framebuffer Multiple Render Targets (MRT)
+ * <p>
+ * OpenGL: Renderer exposes the GL_ARB_draw_buffers extension
+ */
+ FrameBufferMRT,
+
+ /**
+ * Supports framebuffer multi-sampling
+ * <p>
+ * OpenGL: Renderer exposes the GL EXT framebuffer multisample extension<br>
+ * OpenGL ES: Renderer exposes GL_APPLE_framebuffer_multisample or
+ * GL_ANGLE_framebuffer_multisample.
+ */
+ FrameBufferMultisample,
+
+ /**
+ * Supports texture multi-sampling
+ * <p>
+ * OpenGL: Renderer exposes the GL_ARB_texture_multisample extension<br>
+ * OpenGL ES: Renderer exposes the GL_IMG_multisampled_render_to_texture
+ * extension.
+ */
+ TextureMultisample,
+
+ /**
+ * Supports OpenGL 2.0 or OpenGL ES 2.0.
+ */
+ OpenGL20,
+
+ /**
+ * Supports OpenGL 2.1
+ */
+ OpenGL21,
+
+ /**
+ * Supports OpenGL 3.0
+ */
+ OpenGL30,
+
+ /**
+ * Supports OpenGL 3.1
+ */
+ OpenGL31,
+
+ /**
+ * Supports OpenGL 3.2
+ */
+ OpenGL32,
+
+ /**
+ * Supports OpenGL ARB program.
+ * <p>
+ * OpenGL: Renderer exposes ARB_vertex_program and ARB_fragment_program
+ * extensions.
+ */
+ ARBprogram,
+
+ /**
+ * Supports GLSL 1.0
+ */
+ GLSL100,
+
+ /**
+ * Supports GLSL 1.1
+ */
+ GLSL110,
+
+ /**
+ * Supports GLSL 1.2
+ */
+ GLSL120,
+
+ /**
+ * Supports GLSL 1.3
+ */
+ GLSL130,
+
+ /**
+ * Supports GLSL 1.4
+ */
+ GLSL140,
+
+ /**
+ * Supports GLSL 1.5
+ */
+ GLSL150,
+
+ /**
+ * Supports GLSL 3.3
+ */
+ GLSL330,
+
+ /**
+ * Supports reading from textures inside the vertex shader.
+ */
+ VertexTextureFetch,
+
+ /**
+ * Supports geometry shader.
+ */
+ GeometryShader,
+
+ /**
+ * Supports texture arrays
+ */
+ TextureArray,
+
+ /**
+ * Supports texture buffers
+ */
+ TextureBuffer,
+
+ /**
+ * Supports floating point textures (Format.RGB16F)
+ */
+ FloatTexture,
+
+ /**
+ * Supports floating point FBO color buffers (Format.RGB16F)
+ */
+ FloatColorBuffer,
+
+ /**
+ * Supports floating point depth buffer
+ */
+ FloatDepthBuffer,
+
+ /**
+ * Supports Format.RGB111110F for textures
+ */
+ PackedFloatTexture,
+
+ /**
+ * Supports Format.RGB9E5 for textures
+ */
+ SharedExponentTexture,
+
+ /**
+ * Supports Format.RGB111110F for FBO color buffers
+ */
+ PackedFloatColorBuffer,
+
+ /**
+ * Supports Format.RGB9E5 for FBO color buffers
+ */
+ SharedExponentColorBuffer,
+
+ /**
+ * Supports Format.LATC for textures, this includes
+ * support for ATI's 3Dc texture compression.
+ */
+ TextureCompressionLATC,
+
+ /**
+ * Supports Non-Power-Of-Two (NPOT) textures and framebuffers
+ */
+ NonPowerOfTwoTextures,
+
+ /// Vertex Buffer features
+ MeshInstancing,
+
+ /**
+ * Supports VAO, or vertex buffer arrays
+ */
+ VertexBufferArray,
+
+ /**
+ * Supports multisampling on the screen
+ */
+ Multisample;
+
+ /**
+ * Returns true if given the renderer capabilities, the texture
+ * can be supported by the renderer.
+ * <p>
+ * This only checks the format of the texture, non-power-of-2
+ * textures are scaled automatically inside the renderer
+ * if are not supported natively.
+ *
+ * @param caps The collection of renderer capabilities {@link Renderer#getCaps() }.
+ * @param tex The texture to check
+ * @return True if it is supported, false otherwise.
+ */
+ public static boolean supports(Collection<Caps> caps, Texture tex){
+ if (tex.getType() == Texture.Type.TwoDimensionalArray
+ && !caps.contains(Caps.TextureArray))
+ return false;
+
+ Image img = tex.getImage();
+ if (img == null)
+ return true;
+
+ Format fmt = img.getFormat();
+ switch (fmt){
+ case Depth32F:
+ return caps.contains(Caps.FloatDepthBuffer);
+ case LATC:
+ return caps.contains(Caps.TextureCompressionLATC);
+ case RGB16F_to_RGB111110F:
+ case RGB111110F:
+ return caps.contains(Caps.PackedFloatTexture);
+ case RGB16F_to_RGB9E5:
+ case RGB9E5:
+ return caps.contains(Caps.SharedExponentTexture);
+ default:
+ if (fmt.isFloatingPont())
+ return caps.contains(Caps.FloatTexture);
+
+ return true;
+ }
+ }
+
+ /**
+ * Returns true if given the renderer capabilities, the framebuffer
+ * can be supported by the renderer.
+ *
+ * @param caps The collection of renderer capabilities {@link Renderer#getCaps() }.
+ * @param fb The framebuffer to check
+ * @return True if it is supported, false otherwise.
+ */
+ public static boolean supports(Collection<Caps> caps, FrameBuffer fb){
+ if (!caps.contains(Caps.FrameBuffer))
+ return false;
+
+ if (fb.getSamples() > 1
+ && !caps.contains(Caps.FrameBufferMultisample))
+ return false;
+
+ RenderBuffer colorBuf = fb.getColorBuffer();
+ RenderBuffer depthBuf = fb.getDepthBuffer();
+
+ if (depthBuf != null){
+ Format depthFmt = depthBuf.getFormat();
+ if (!depthFmt.isDepthFormat()){
+ return false;
+ }else{
+ if (depthFmt == Format.Depth32F
+ && !caps.contains(Caps.FloatDepthBuffer))
+ return false;
+ }
+ }
+ if (colorBuf != null){
+ Format colorFmt = colorBuf.getFormat();
+ if (colorFmt.isDepthFormat())
+ return false;
+
+ if (colorFmt.isCompressed())
+ return false;
+
+ switch (colorFmt){
+ case RGB111110F:
+ return caps.contains(Caps.PackedFloatColorBuffer);
+ case RGB16F_to_RGB111110F:
+ case RGB16F_to_RGB9E5:
+ case RGB9E5:
+ return false;
+ default:
+ if (colorFmt.isFloatingPont())
+ return caps.contains(Caps.FloatColorBuffer);
+
+ return true;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if given the renderer capabilities, the shader
+ * can be supported by the renderer.
+ *
+ * @param caps The collection of renderer capabilities {@link Renderer#getCaps() }.
+ * @param shader The shader to check
+ * @return True if it is supported, false otherwise.
+ */
+ public static boolean supports(Collection<Caps> caps, Shader shader){
+ String lang = shader.getLanguage();
+ if (lang.startsWith("GLSL")){
+ int ver = Integer.parseInt(lang.substring(4));
+ switch (ver){
+ case 100:
+ return caps.contains(Caps.GLSL100);
+ case 110:
+ return caps.contains(Caps.GLSL110);
+ case 120:
+ return caps.contains(Caps.GLSL120);
+ case 130:
+ return caps.contains(Caps.GLSL130);
+ case 140:
+ return caps.contains(Caps.GLSL140);
+ case 150:
+ return caps.contains(Caps.GLSL150);
+ case 330:
+ return caps.contains(Caps.GLSL330);
+ default:
+ return false;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/GL1Renderer.java b/engine/src/core/com/jme3/renderer/GL1Renderer.java
new file mode 100644
index 0000000..78df0c6
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/GL1Renderer.java
@@ -0,0 +1,26 @@
+package com.jme3.renderer;
+
+import com.jme3.material.FixedFuncBinding;
+
+/**
+ * Renderer sub-interface that is used for non-shader based renderers.
+ * <p>
+ * The <code>GL1Renderer</code> provides a single call,
+ * {@link #setFixedFuncBinding(com.jme3.material.FixedFuncBinding, java.lang.Object) }
+ * which allows to set fixed functionality state.
+ *
+ * @author Kirill Vainer
+ */
+public interface GL1Renderer extends Renderer {
+
+ /**
+ * Set the fixed functionality state.
+ * <p>
+ * See {@link FixedFuncBinding} for various values that
+ * can be set.
+ *
+ * @param ffBinding The binding to set
+ * @param val The value
+ */
+ public void setFixedFuncBinding(FixedFuncBinding ffBinding, Object val);
+}
diff --git a/engine/src/core/com/jme3/renderer/IDList.java b/engine/src/core/com/jme3/renderer/IDList.java
new file mode 100644
index 0000000..2db7294
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/IDList.java
@@ -0,0 +1,121 @@
+/*
+ * 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 java.util.Arrays;
+
+/**
+ * A specialized data-structure used to optimize state changes of "slot"
+ * based state.
+ */
+public class IDList {
+
+ public int[] newList = new int[16];
+ public int[] oldList = new int[16];
+ public int newLen = 0;
+ public int oldLen = 0;
+
+ /**
+ * Reset all states to zero
+ */
+ public void reset(){
+ newLen = 0;
+ oldLen = 0;
+ Arrays.fill(newList, 0);
+ Arrays.fill(oldList, 0);
+ }
+
+ /**
+ * Adds an index to the new list.
+ * If the index was not in the old list, false is returned,
+ * if the index was in the old list, it is removed from the old
+ * list and true is returned.
+ *
+ * @param idx The index to move
+ * @return True if it existed in old list and was removed
+ * from there, false otherwise.
+ */
+ public boolean moveToNew(int idx){
+ if (newLen == 0 || newList[newLen-1] != idx)
+ // add item to newList first
+ newList[newLen++] = idx;
+
+ // find idx in oldList, if removed successfuly, return true.
+ for (int i = 0; i < oldLen; i++){
+ if (oldList[i] == idx){
+ // found index in slot i
+ // delete index from old list
+ oldLen --;
+ for (int j = i; j < oldLen; j++){
+ oldList[j] = oldList[j+1];
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Copies the new list to the old list, and clears the new list.
+ */
+ public void copyNewToOld(){
+ System.arraycopy(newList, 0, oldList, 0, newLen);
+ oldLen = newLen;
+ newLen = 0;
+ }
+
+ /**
+ * Prints the contents of the lists
+ */
+ public void print(){
+ if (newLen > 0){
+ System.out.print("New List: ");
+ for (int i = 0; i < newLen; i++){
+ if (i == newLen -1)
+ System.out.println(newList[i]);
+ else
+ System.out.print(newList[i]+", ");
+ }
+ }
+ if (oldLen > 0){
+ System.out.print("Old List: ");
+ for (int i = 0; i < oldLen; i++){
+ if (i == oldLen -1)
+ System.out.println(oldList[i]);
+ else
+ System.out.print(oldList[i]+", ");
+ }
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/RenderContext.java b/engine/src/core/com/jme3/renderer/RenderContext.java
new file mode 100644
index 0000000..ee9ff9c
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/RenderContext.java
@@ -0,0 +1,319 @@
+/*
+ * 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.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+
+/**
+ * Represents the current state of the graphics library. This class is used
+ * internally to reduce state changes. NOTE: This class is specific to OpenGL.
+ */
+public class RenderContext {
+
+ /**
+ * @see RenderState#setFaceCullMode(com.jme3.material.RenderState.FaceCullMode)
+ */
+ public RenderState.FaceCullMode cullMode = RenderState.FaceCullMode.Off;
+
+ /**
+ * @see RenderState#setDepthTest(boolean)
+ */
+ public boolean depthTestEnabled = false;
+
+ /**
+ * @see RenderState#setAlphaTest(boolean)
+ */
+ public boolean alphaTestEnabled = false;
+
+ /**
+ * @see RenderState#setDepthWrite(boolean)
+ */
+ public boolean depthWriteEnabled = true;
+
+ /**
+ * @see RenderState#setColorWrite(boolean)
+ */
+ public boolean colorWriteEnabled = true;
+
+ /**
+ * @see Renderer#setClipRect(int, int, int, int)
+ */
+ public boolean clipRectEnabled = false;
+
+ /**
+ * @see RenderState#setPolyOffset(float, float)
+ */
+ public boolean polyOffsetEnabled = false;
+
+ /**
+ * @see RenderState#setPolyOffset(float, float)
+ */
+ public float polyOffsetFactor = 0;
+
+ /**
+ * @see RenderState#setPolyOffset(float, float)
+ */
+ public float polyOffsetUnits = 0;
+
+ /**
+ * For normals only. Uses GL_NORMALIZE.
+ *
+ * @see VertexBuffer#setNormalized(boolean)
+ */
+ public boolean normalizeEnabled = false;
+
+ /**
+ * For glMatrixMode.
+ *
+ * @see Renderer#setWorldMatrix(com.jme3.math.Matrix4f)
+ * @see Renderer#setViewProjectionMatrices(com.jme3.math.Matrix4f, com.jme3.math.Matrix4f)
+ */
+ public int matrixMode = -1;
+
+ /**
+ * @see Mesh#setPointSize(float)
+ */
+ public float pointSize = 1;
+
+ /**
+ * @see Mesh#setLineWidth(float)
+ */
+ public float lineWidth = 1;
+
+ /**
+ * @see RenderState#setBlendMode(com.jme3.material.RenderState.BlendMode)
+ */
+ public RenderState.BlendMode blendMode = RenderState.BlendMode.Off;
+
+ /**
+ * @see RenderState#setWireframe(boolean)
+ */
+ public boolean wireframe = false;
+
+ /**
+ * @see RenderState#setPointSprite(boolean)
+ */
+ public boolean pointSprite = false;
+
+ /**
+ * @see Renderer#setShader(com.jme3.shader.Shader)
+ */
+ public int boundShaderProgram;
+
+ /**
+ * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer)
+ */
+ public int boundFBO = 0;
+
+ /**
+ * Currently bound Renderbuffer
+ *
+ * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer)
+ */
+ public int boundRB = 0;
+
+ /**
+ * Currently bound draw buffer
+ * -2 = GL_NONE
+ * -1 = GL_BACK
+ * 0 = GL_COLOR_ATTACHMENT0
+ * n = GL_COLOR_ATTACHMENTn
+ * where n is an integer greater than 1
+ *
+ * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer)
+ * @see FrameBuffer#setTargetIndex(int)
+ */
+ public int boundDrawBuf = -1;
+
+ /**
+ * Currently bound read buffer
+ *
+ * @see RenderContext#boundDrawBuf
+ * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer)
+ * @see FrameBuffer#setTargetIndex(int)
+ */
+ public int boundReadBuf = -1;
+
+ /**
+ * Currently bound element array vertex buffer.
+ *
+ * @see Renderer#renderMesh(com.jme3.scene.Mesh, int, int)
+ */
+ public int boundElementArrayVBO;
+
+ /**
+ * @see Renderer#renderMesh(com.jme3.scene.Mesh, int, int)
+ */
+ public int boundVertexArray;
+
+ /**
+ * Currently bound array vertex buffer.
+ *
+ * @see Renderer#renderMesh(com.jme3.scene.Mesh, int, int)
+ */
+ public int boundArrayVBO;
+
+ public int numTexturesSet = 0;
+
+ /**
+ * Current bound texture IDs for each texture unit.
+ *
+ * @see Renderer#setTexture(int, com.jme3.texture.Texture)
+ */
+ public Image[] boundTextures = new Image[16];
+
+ /**
+ * IDList for texture units
+ *
+ * @see Renderer#setTexture(int, com.jme3.texture.Texture)
+ */
+ public IDList textureIndexList = new IDList();
+
+ /**
+ * Currently bound texture unit
+ *
+ * @see Renderer#setTexture(int, com.jme3.texture.Texture)
+ */
+ public int boundTextureUnit = 0;
+
+ /**
+ * Stencil Buffer state
+ */
+ public boolean stencilTest = false;
+ public RenderState.StencilOperation frontStencilStencilFailOperation = RenderState.StencilOperation.Keep;
+ public RenderState.StencilOperation frontStencilDepthFailOperation = RenderState.StencilOperation.Keep;
+ public RenderState.StencilOperation frontStencilDepthPassOperation = RenderState.StencilOperation.Keep;
+ public RenderState.StencilOperation backStencilStencilFailOperation = RenderState.StencilOperation.Keep;
+ public RenderState.StencilOperation backStencilDepthFailOperation = RenderState.StencilOperation.Keep;
+ public RenderState.StencilOperation backStencilDepthPassOperation = RenderState.StencilOperation.Keep;
+ public RenderState.TestFunction frontStencilFunction = RenderState.TestFunction.Always;
+ public RenderState.TestFunction backStencilFunction = RenderState.TestFunction.Always;
+
+ /**
+ * Vertex attribs currently bound and enabled. If a slot is null, then
+ * it is disabled.
+ */
+ public VertexBuffer[] boundAttribs = new VertexBuffer[16];
+
+ /**
+ * IDList for vertex attributes
+ */
+ public IDList attribIndexList = new IDList();
+
+ /**
+ * Ambient color (GL1 only)
+ */
+ public ColorRGBA ambient;
+
+ /**
+ * Diffuse color (GL1 only)
+ */
+ public ColorRGBA diffuse;
+
+ /**
+ * Specular color (GL1 only)
+ */
+ public ColorRGBA specular;
+
+ /**
+ * Material color (GL1 only)
+ */
+ public ColorRGBA color;
+
+ /**
+ * Shininess (GL1 only)
+ */
+ public float shininess;
+
+ /**
+ * Use vertex color (GL1 only)
+ */
+ public boolean useVertexColor;
+
+ /**
+ * Reset the RenderContext to default GL state
+ */
+ public void reset(){
+ cullMode = RenderState.FaceCullMode.Off;
+ depthTestEnabled = false;
+ alphaTestEnabled = false;
+ depthWriteEnabled = false;
+ colorWriteEnabled = false;
+ clipRectEnabled = false;
+ polyOffsetEnabled = false;
+ polyOffsetFactor = 0;
+ polyOffsetUnits = 0;
+ normalizeEnabled = false;
+ matrixMode = -1;
+ pointSize = 1;
+ blendMode = RenderState.BlendMode.Off;
+ wireframe = false;
+ boundShaderProgram = 0;
+ boundFBO = 0;
+ boundRB = 0;
+ boundDrawBuf = -1;
+ boundReadBuf = -1;
+ boundElementArrayVBO = 0;
+ boundVertexArray = 0;
+ boundArrayVBO = 0;
+ numTexturesSet = 0;
+ for (int i = 0; i < boundTextures.length; i++)
+ boundTextures[i] = null;
+
+ textureIndexList.reset();
+ boundTextureUnit = 0;
+ for (int i = 0; i < boundAttribs.length; i++)
+ boundAttribs[i] = null;
+
+ attribIndexList.reset();
+
+ stencilTest = false;
+ frontStencilStencilFailOperation = RenderState.StencilOperation.Keep;
+ frontStencilDepthFailOperation = RenderState.StencilOperation.Keep;
+ frontStencilDepthPassOperation = RenderState.StencilOperation.Keep;
+ backStencilStencilFailOperation = RenderState.StencilOperation.Keep;
+ backStencilDepthFailOperation = RenderState.StencilOperation.Keep;
+ backStencilDepthPassOperation = RenderState.StencilOperation.Keep;
+ frontStencilFunction = RenderState.TestFunction.Always;
+ backStencilFunction = RenderState.TestFunction.Always;
+
+ ambient = diffuse = specular = color = null;
+ shininess = 0;
+ useVertexColor = false;
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/RenderManager.java b/engine/src/core/com/jme3/renderer/RenderManager.java
new file mode 100644
index 0000000..1d58d22
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/RenderManager.java
@@ -0,0 +1,1170 @@
+/*
+ * 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.renderer;
+
+import com.jme3.material.Material;
+import com.jme3.material.MaterialDef;
+import com.jme3.material.RenderState;
+import com.jme3.material.Technique;
+import com.jme3.math.*;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.*;
+import com.jme3.shader.Uniform;
+import com.jme3.shader.UniformBinding;
+import com.jme3.shader.VarType;
+import com.jme3.system.NullRenderer;
+import com.jme3.system.Timer;
+import com.jme3.util.IntMap.Entry;
+import com.jme3.util.TempVars;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * <code>RenderManager</code> is a high-level rendering interface that is
+ * above the Renderer implementation. RenderManager takes care
+ * of rendering the scene graphs attached to each viewport and
+ * handling SceneProcessors.
+ *
+ * @see SceneProcessor
+ * @see ViewPort
+ * @see Spatial
+ */
+public class RenderManager {
+
+ private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
+
+ private Renderer renderer;
+ private Timer timer;
+ private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();
+ private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
+ private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();
+ private Camera prevCam = null;
+ private Material forcedMaterial = null;
+ private String forcedTechnique = null;
+ private RenderState forcedRenderState = null;
+ private boolean shader;
+ private int viewX, viewY, viewWidth, viewHeight;
+ private float near, far;
+ private Matrix4f orthoMatrix = new Matrix4f();
+ private Matrix4f viewMatrix = new Matrix4f();
+ private Matrix4f projMatrix = new Matrix4f();
+ private Matrix4f viewProjMatrix = new Matrix4f();
+ private Matrix4f worldMatrix = new Matrix4f();
+ private Vector3f camUp = new Vector3f(),
+ camLeft = new Vector3f(),
+ camDir = new Vector3f(),
+ camLoc = new Vector3f();
+ //temp technique
+ private String tmpTech;
+ private boolean handleTranlucentBucket = true;
+
+ /**
+ * Create a high-level rendering interface over the
+ * low-level rendering interface.
+ * @param renderer
+ */
+ public RenderManager(Renderer renderer) {
+ this.renderer = renderer;
+ //this.shader = renderer.getCaps().contains(Caps.GLSL100);
+ }
+
+ /**
+ * Returns the pre ViewPort with the given name.
+ *
+ * @param viewName The name of the pre ViewPort to look up
+ * @return The ViewPort, or null if not found.
+ *
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public ViewPort getPreView(String viewName) {
+ for (int i = 0; i < preViewPorts.size(); i++) {
+ if (preViewPorts.get(i).getName().equals(viewName)) {
+ return preViewPorts.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the specified pre ViewPort.
+ *
+ * @param view The pre ViewPort to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removePreView(ViewPort view) {
+ return preViewPorts.remove(view);
+ }
+
+ /**
+ * Returns the main ViewPort with the given name.
+ *
+ * @param viewName The name of the main ViewPort to look up
+ * @return The ViewPort, or null if not found.
+ *
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public ViewPort getMainView(String viewName) {
+ for (int i = 0; i < viewPorts.size(); i++) {
+ if (viewPorts.get(i).getName().equals(viewName)) {
+ return viewPorts.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the main ViewPort with the specified name.
+ *
+ * @param viewName The main ViewPort name to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removeMainView(String viewName) {
+ for (int i = 0; i < viewPorts.size(); i++) {
+ if (viewPorts.get(i).getName().equals(viewName)) {
+ viewPorts.remove(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes the specified main ViewPort.
+ *
+ * @param view The main ViewPort to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removeMainView(ViewPort view) {
+ return viewPorts.remove(view);
+ }
+
+ /**
+ * Returns the post ViewPort with the given name.
+ *
+ * @param viewName The name of the post ViewPort to look up
+ * @return The ViewPort, or null if not found.
+ *
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public ViewPort getPostView(String viewName) {
+ for (int i = 0; i < postViewPorts.size(); i++) {
+ if (postViewPorts.get(i).getName().equals(viewName)) {
+ return postViewPorts.get(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Removes the post ViewPort with the specified name.
+ *
+ * @param viewName The post ViewPort name to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removePostView(String viewName) {
+ for (int i = 0; i < postViewPorts.size(); i++) {
+ if (postViewPorts.get(i).getName().equals(viewName)) {
+ postViewPorts.remove(i);
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes the specified post ViewPort.
+ *
+ * @param view The post ViewPort to remove
+ * @return True if the ViewPort was removed successfully.
+ *
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public boolean removePostView(ViewPort view) {
+ return postViewPorts.remove(view);
+ }
+
+ /**
+ * Returns a read-only list of all pre ViewPorts
+ * @return a read-only list of all pre ViewPorts
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public List<ViewPort> getPreViews() {
+ return Collections.unmodifiableList(preViewPorts);
+ }
+
+ /**
+ * Returns a read-only list of all main ViewPorts
+ * @return a read-only list of all main ViewPorts
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public List<ViewPort> getMainViews() {
+ return Collections.unmodifiableList(viewPorts);
+ }
+
+ /**
+ * Returns a read-only list of all post ViewPorts
+ * @return a read-only list of all post ViewPorts
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public List<ViewPort> getPostViews() {
+ return Collections.unmodifiableList(postViewPorts);
+ }
+
+ /**
+ * Creates a new pre ViewPort, to display the given camera's content.
+ * <p>
+ * The view will be processed before the main and post viewports.
+ */
+ public ViewPort createPreView(String viewName, Camera cam) {
+ ViewPort vp = new ViewPort(viewName, cam);
+ preViewPorts.add(vp);
+ return vp;
+ }
+
+ /**
+ * Creates a new main ViewPort, to display the given camera's content.
+ * <p>
+ * The view will be processed before the post viewports but after
+ * the pre viewports.
+ */
+ public ViewPort createMainView(String viewName, Camera cam) {
+ ViewPort vp = new ViewPort(viewName, cam);
+ viewPorts.add(vp);
+ return vp;
+ }
+
+ /**
+ * Creates a new post ViewPort, to display the given camera's content.
+ * <p>
+ * The view will be processed after the pre and main viewports.
+ */
+ public ViewPort createPostView(String viewName, Camera cam) {
+ ViewPort vp = new ViewPort(viewName, cam);
+ postViewPorts.add(vp);
+ return vp;
+ }
+
+ private void notifyReshape(ViewPort vp, int w, int h) {
+ List<SceneProcessor> processors = vp.getProcessors();
+ for (SceneProcessor proc : processors) {
+ if (!proc.isInitialized()) {
+ proc.initialize(this, vp);
+ } else {
+ proc.reshape(vp, w, h);
+ }
+ }
+ }
+
+ /**
+ * Internal use only.
+ * Updates the resolution of all on-screen cameras to match
+ * the given width and height.
+ */
+ public void notifyReshape(int w, int h) {
+ for (ViewPort vp : preViewPorts) {
+ if (vp.getOutputFrameBuffer() == null) {
+ Camera cam = vp.getCamera();
+ cam.resize(w, h, true);
+ }
+ notifyReshape(vp, w, h);
+ }
+ for (ViewPort vp : viewPorts) {
+ if (vp.getOutputFrameBuffer() == null) {
+ Camera cam = vp.getCamera();
+ cam.resize(w, h, true);
+ }
+ notifyReshape(vp, w, h);
+ }
+ for (ViewPort vp : postViewPorts) {
+ if (vp.getOutputFrameBuffer() == null) {
+ Camera cam = vp.getCamera();
+ cam.resize(w, h, true);
+ }
+ notifyReshape(vp, w, h);
+ }
+ }
+
+ /**
+ * Internal use only.
+ * Updates the given list of uniforms with {@link UniformBinding uniform bindings}
+ * based on the current world state.
+ */
+ public void updateUniformBindings(List<Uniform> params) {
+ // assums worldMatrix is properly set.
+ TempVars vars = TempVars.get();
+
+ Matrix4f tempMat4 = vars.tempMat4;
+ Matrix3f tempMat3 = vars.tempMat3;
+ Vector2f tempVec2 = vars.vect2d;
+ Quaternion tempVec4 = vars.quat1;
+
+ for (int i = 0; i < params.size(); i++) {
+ Uniform u = params.get(i);
+ switch (u.getBinding()) {
+ case WorldMatrix:
+ u.setValue(VarType.Matrix4, worldMatrix);
+ break;
+ case ViewMatrix:
+ u.setValue(VarType.Matrix4, viewMatrix);
+ break;
+ case ProjectionMatrix:
+ u.setValue(VarType.Matrix4, projMatrix);
+ break;
+ case ViewProjectionMatrix:
+ u.setValue(VarType.Matrix4, viewProjMatrix);
+ break;
+ case WorldViewMatrix:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case NormalMatrix:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.toRotationMatrix(tempMat3);
+ tempMat3.invertLocal();
+ tempMat3.transposeLocal();
+ u.setValue(VarType.Matrix3, tempMat3);
+ break;
+ case WorldViewProjectionMatrix:
+ tempMat4.set(viewProjMatrix);
+ tempMat4.multLocal(worldMatrix);
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case WorldMatrixInverse:
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ViewMatrixInverse:
+ tempMat4.set(viewMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ProjectionMatrixInverse:
+ tempMat4.set(projMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ViewProjectionMatrixInverse:
+ tempMat4.set(viewProjMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case WorldViewMatrixInverse:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case NormalMatrixInverse:
+ tempMat4.set(viewMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.toRotationMatrix(tempMat3);
+ tempMat3.invertLocal();
+ tempMat3.transposeLocal();
+ tempMat3.invertLocal();
+ u.setValue(VarType.Matrix3, tempMat3);
+ break;
+ case WorldViewProjectionMatrixInverse:
+ tempMat4.set(viewProjMatrix);
+ tempMat4.multLocal(worldMatrix);
+ tempMat4.invertLocal();
+ u.setValue(VarType.Matrix4, tempMat4);
+ break;
+ case ViewPort:
+ tempVec4.set(viewX, viewY, viewWidth, viewHeight);
+ u.setValue(VarType.Vector4, tempVec4);
+ break;
+ case Resolution:
+ tempVec2.set(viewWidth, viewHeight);
+ u.setValue(VarType.Vector2, tempVec2);
+ break;
+ case Aspect:
+ float aspect = ((float) viewWidth) / viewHeight;
+ u.setValue(VarType.Float, aspect);
+ break;
+ case FrustumNearFar:
+ tempVec2.set(near, far);
+ u.setValue(VarType.Vector2, tempVec2);
+ break;
+ case CameraPosition:
+ u.setValue(VarType.Vector3, camLoc);
+ break;
+ case CameraDirection:
+ u.setValue(VarType.Vector3, camDir);
+ break;
+ case CameraLeft:
+ u.setValue(VarType.Vector3, camLeft);
+ break;
+ case CameraUp:
+ u.setValue(VarType.Vector3, camUp);
+ break;
+ case Time:
+ u.setValue(VarType.Float, timer.getTimeInSeconds());
+ break;
+ case Tpf:
+ u.setValue(VarType.Float, timer.getTimePerFrame());
+ break;
+ case FrameRate:
+ u.setValue(VarType.Float, timer.getFrameRate());
+ break;
+ }
+ }
+
+ vars.release();
+ }
+
+ /**
+ * Set the material to use to render all future objects.
+ * This overrides the material set on the geometry and renders
+ * with the provided material instead.
+ * Use null to clear the material and return renderer to normal
+ * functionality.
+ * @param mat The forced material to set, or null to return to normal
+ */
+ public void setForcedMaterial(Material mat) {
+ forcedMaterial = mat;
+ }
+
+ /**
+ * Returns the forced render state previously set with
+ * {@link #setForcedRenderState(com.jme3.material.RenderState) }.
+ * @return the forced render state
+ */
+ public RenderState getForcedRenderState() {
+ return forcedRenderState;
+ }
+
+ /**
+ * Set the render state to use for all future objects.
+ * This overrides the render state set on the material and instead
+ * forces this render state to be applied for all future materials
+ * rendered. Set to null to return to normal functionality.
+ *
+ * @param forcedRenderState The forced render state to set, or null
+ * to return to normal
+ */
+ public void setForcedRenderState(RenderState forcedRenderState) {
+ this.forcedRenderState = forcedRenderState;
+ }
+
+ /**
+ * Set the timer that should be used to query the time based
+ * {@link UniformBinding}s for material world parameters.
+ *
+ * @param timer The timer to query time world parameters
+ */
+ public void setTimer(Timer timer) {
+ this.timer = timer;
+ }
+
+ /**
+ * Returns the forced technique name set.
+ *
+ * @return the forced technique name set.
+ *
+ * @see #setForcedTechnique(java.lang.String)
+ */
+ public String getForcedTechnique() {
+ return forcedTechnique;
+ }
+
+ /**
+ * Sets the forced technique to use when rendering geometries.
+ * <p>
+ * If the specified technique name is available on the geometry's
+ * material, then it is used, otherwise, the
+ * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used.
+ * If a forced material is not set and the forced technique name cannot
+ * be found on the material, the geometry will <em>not</em> be rendered.
+ *
+ * @param forcedTechnique The forced technique name to use, set to null
+ * to return to normal functionality.
+ *
+ * @see #renderGeometry(com.jme3.scene.Geometry)
+ */
+ public void setForcedTechnique(String forcedTechnique) {
+ this.forcedTechnique = forcedTechnique;
+ }
+
+ /**
+ * Enable or disable alpha-to-coverage.
+ * <p>
+ * When alpha to coverage is enabled and the renderer implementation
+ * supports it, then alpha blending will be replaced with alpha dissolve
+ * if multi-sampling is also set on the renderer.
+ * This feature allows avoiding of alpha blending artifacts due to
+ * lack of triangle-level back-to-front sorting.
+ *
+ * @param value True to enable alpha-to-coverage, false otherwise.
+ */
+ public void setAlphaToCoverage(boolean value) {
+ renderer.setAlphaToCoverage(value);
+ }
+
+ /**
+ * True if the translucent bucket should automatically be rendered
+ * by the RenderManager.
+ *
+ * @return Whether or not the translucent bucket is rendered.
+ *
+ * @see #setHandleTranslucentBucket(boolean)
+ */
+ public boolean isHandleTranslucentBucket() {
+ return handleTranlucentBucket;
+ }
+
+ /**
+ * Enable or disable rendering of the
+ * {@link Bucket#Translucent translucent bucket}
+ * by the RenderManager. The default is enabled.
+ *
+ * @param handleTranslucentBucket Whether or not the translucent bucket should
+ * be rendered.
+ */
+ public void setHandleTranslucentBucket(boolean handleTranslucentBucket) {
+ this.handleTranlucentBucket = handleTranslucentBucket;
+ }
+
+ /**
+ * Internal use only. Sets the world matrix to use for future
+ * rendering. This has no effect unless objects are rendered manually
+ * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }.
+ * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will
+ * override this value.
+ *
+ * @param mat The world matrix to set
+ */
+ public void setWorldMatrix(Matrix4f mat) {
+ if (shader) {
+ worldMatrix.set(mat);
+ } else {
+ renderer.setWorldMatrix(mat);
+ }
+ }
+
+ /**
+ * Renders the given geometry.
+ * <p>
+ * First the proper world matrix is set, if
+ * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform}
+ * feature is enabled, the identity world matrix is used, otherwise, the
+ * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used.
+ * <p>
+ * Once the world matrix is applied, the proper material is chosen for rendering.
+ * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is
+ * set on this RenderManager, then it is used for rendering the geometry,
+ * otherwise, the {@link Geometry#getMaterial() geometry's material} is used.
+ * <p>
+ * If a {@link #setForcedTechnique(java.lang.String) forced technique} is
+ * set on this RenderManager, then it is selected automatically
+ * on the geometry's material and is used for rendering. Otherwise, one
+ * of the {@link MaterialDef#getDefaultTechniques() default techniques} is
+ * used.
+ * <p>
+ * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced
+ * render state} is set on this RenderManager, then it is used
+ * for rendering the material, and the material's own render state is ignored.
+ * Otherwise, the material's render state is used as intended.
+ *
+ * @param g The geometry to render
+ *
+ * @see Technique
+ * @see RenderState
+ * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
+ * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
+ */
+ public void renderGeometry(Geometry g) {
+ if (g.isIgnoreTransform()) {
+ setWorldMatrix(Matrix4f.IDENTITY);
+ } else {
+ setWorldMatrix(g.getWorldMatrix());
+ }
+
+ //if forcedTechnique we try to force it for render,
+ //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
+ //else the geom is not rendered
+ if (forcedTechnique != null) {
+ if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {
+ tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";
+ g.getMaterial().selectTechnique(forcedTechnique, this);
+ // use geometry's material
+ g.getMaterial().render(g, this);
+ g.getMaterial().selectTechnique(tmpTech, this);
+ //Reverted this part from revision 6197
+ //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered
+ } else if (forcedMaterial != null) {
+ // use forced material
+ forcedMaterial.render(g, this);
+ }
+ } else if (forcedMaterial != null) {
+ // use forced material
+ forcedMaterial.render(g, this);
+ } else {
+ g.getMaterial().render(g, this);
+ }
+ }
+
+ /**
+ * Renders the given GeometryList.
+ * <p>
+ * For every geometry in the list, the
+ * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called.
+ *
+ * @param gl The geometry list to render.
+ *
+ * @see GeometryList
+ * @see #renderGeometry(com.jme3.scene.Geometry)
+ */
+ public void renderGeometryList(GeometryList gl) {
+ for (int i = 0; i < gl.size(); i++) {
+ renderGeometry(gl.get(i));
+ }
+ }
+
+ /**
+ * If a spatial is not inside the eye frustum, it
+ * is still rendered in the shadow frustum (shadow casting queue)
+ * through this recursive method.
+ */
+ private void renderShadow(Spatial s, RenderQueue rq) {
+ if (s instanceof Node) {
+ Node n = (Node) s;
+ List<Spatial> children = n.getChildren();
+ for (int i = 0; i < children.size(); i++) {
+ renderShadow(children.get(i), rq);
+ }
+ } else if (s instanceof Geometry) {
+ Geometry gm = (Geometry) s;
+
+ RenderQueue.ShadowMode shadowMode = s.getShadowMode();
+ if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {
+ //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
+ rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
+ }
+ }
+ }
+
+ /**
+ * Preloads a scene for rendering.
+ * <p>
+ * After invocation of this method, the underlying
+ * renderer would have uploaded any textures, shaders and meshes
+ * used by the given scene to the video driver.
+ * Using this method is useful when wishing to avoid the initial pause
+ * when rendering a scene for the first time. Note that it is not
+ * guaranteed that the underlying renderer will actually choose to upload
+ * the data to the GPU so some pause is still to be expected.
+ *
+ * @param scene The scene to preload
+ */
+ public void preloadScene(Spatial scene) {
+ if (scene instanceof Node) {
+ // recurse for all children
+ Node n = (Node) scene;
+ List<Spatial> children = n.getChildren();
+ for (int i = 0; i < children.size(); i++) {
+ preloadScene(children.get(i));
+ }
+ } else if (scene instanceof Geometry) {
+ // add to the render queue
+ Geometry gm = (Geometry) scene;
+ if (gm.getMaterial() == null) {
+ throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
+ }
+
+ gm.getMaterial().preload(this);
+ Mesh mesh = gm.getMesh();
+ if (mesh != null) {
+ for (Entry<VertexBuffer> entry : mesh.getBuffers()) {
+ VertexBuffer buf = entry.getValue();
+ if (buf.getData() != null) {
+ renderer.updateBufferData(buf);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Flattens the given scene graph into the ViewPort's RenderQueue,
+ * checking for culling as the call goes down the graph recursively.
+ * <p>
+ * First, the scene is checked for culling based on the <code>Spatial</code>s
+ * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint},
+ * if the camera frustum contains the scene, then this method is recursively
+ * called on its children.
+ * <p>
+ * When the scene's leaves or {@link Geometry geometries} are reached,
+ * they are each enqueued into the
+ * {@link ViewPort#getQueue() ViewPort's render queue}.
+ * <p>
+ * In addition to enqueuing the visible geometries, this method
+ * also scenes which cast or receive shadows, by putting them into the
+ * RenderQueue's
+ * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)
+ * shadow queue}. Each Spatial which has its
+ * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
+ * set to not off, will be put into the appropriate shadow queue, note that
+ * this process does not check for frustum culling on any
+ * {@link ShadowMode#Cast shadow casters}, as they don't have to be
+ * in the eye camera frustum to cast shadows on objects that are inside it.
+ *
+ * @param scene The scene to flatten into the queue
+ * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera}
+ * used for culling and the {@link ViewPort#getQueue() queue} used to
+ * contain the flattened scene graph.
+ */
+ public void renderScene(Spatial scene, ViewPort vp) {
+ if (scene.getParent() == null) {
+ vp.getCamera().setPlaneState(0);
+ }
+ // check culling first.
+ if (!scene.checkCulling(vp.getCamera())) {
+ // move on to shadow-only render
+ if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) {
+ renderShadow(scene, vp.getQueue());
+ }
+ return;
+ }
+
+ scene.runControlRender(this, vp);
+ if (scene instanceof Node) {
+ // recurse for all children
+ Node n = (Node) scene;
+ List<Spatial> children = n.getChildren();
+ //saving cam state for culling
+ int camState = vp.getCamera().getPlaneState();
+ for (int i = 0; i < children.size(); i++) {
+ //restoring cam state before proceeding children recusively
+ vp.getCamera().setPlaneState(camState);
+ renderScene(children.get(i), vp);
+
+ }
+ } else if (scene instanceof Geometry) {
+
+ // add to the render queue
+ Geometry gm = (Geometry) scene;
+ if (gm.getMaterial() == null) {
+ throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
+ }
+
+ vp.getQueue().addToQueue(gm, scene.getQueueBucket());
+
+ // add to shadow queue if needed
+ RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
+ if (shadowMode != RenderQueue.ShadowMode.Off) {
+ vp.getQueue().addToShadowQueue(gm, shadowMode);
+ }
+ }
+ }
+
+ /**
+ * Returns the camera currently used for rendering.
+ * <p>
+ * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }.
+ *
+ * @return the camera currently used for rendering.
+ */
+ public Camera getCurrentCamera() {
+ return prevCam;
+ }
+
+ /**
+ * The renderer implementation used for rendering operations.
+ *
+ * @return The renderer implementation
+ *
+ * @see #RenderManager(com.jme3.renderer.Renderer)
+ * @see Renderer
+ */
+ public Renderer getRenderer() {
+ return renderer;
+ }
+
+ /**
+ * Flushes the ViewPort's {@link ViewPort#getQueue() render queue}
+ * by rendering each of its visible buckets.
+ * By default the queues will automatically be cleared after rendering,
+ * so there's no need to clear them manually.
+ *
+ * @param vp The ViewPort of which the queue will be flushed
+ *
+ * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera)
+ * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList)
+ */
+ public void flushQueue(ViewPort vp) {
+ renderViewPortQueues(vp, true);
+ }
+
+ /**
+ * Clears the queue of the given ViewPort.
+ * Simply calls {@link RenderQueue#clear() } on the ViewPort's
+ * {@link ViewPort#getQueue() render queue}.
+ *
+ * @param vp The ViewPort of which the queue will be cleared.
+ *
+ * @see RenderQueue#clear()
+ * @see ViewPort#getQueue()
+ */
+ public void clearQueue(ViewPort vp) {
+ vp.getQueue().clear();
+ }
+
+ /**
+ * Render the given viewport queues.
+ * <p>
+ * Changes the {@link Renderer#setDepthRange(float, float) depth range}
+ * appropriately as expected by each queue and then calls
+ * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) }
+ * on the queue. Makes sure to restore the depth range to [0, 1]
+ * at the end of the call.
+ * Note that the {@link Bucket#Translucent translucent bucket} is NOT
+ * rendered by this method. Instead the user should call
+ * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) }
+ * after this call.
+ *
+ * @param vp the viewport of which queue should be rendered
+ * @param flush If true, the queues will be cleared after
+ * rendering.
+ *
+ * @see RenderQueue
+ * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort)
+ */
+ public void renderViewPortQueues(ViewPort vp, boolean flush) {
+ RenderQueue rq = vp.getQueue();
+ Camera cam = vp.getCamera();
+ boolean depthRangeChanged = false;
+
+ // render opaque objects with default depth range
+ // opaque objects are sorted front-to-back, reducing overdraw
+ rq.renderQueue(Bucket.Opaque, this, cam, flush);
+
+ // render the sky, with depth range set to the farthest
+ if (!rq.isQueueEmpty(Bucket.Sky)) {
+ renderer.setDepthRange(1, 1);
+ rq.renderQueue(Bucket.Sky, this, cam, flush);
+ depthRangeChanged = true;
+ }
+
+
+ // transparent objects are last because they require blending with the
+ // rest of the scene's objects. Consequently, they are sorted
+ // back-to-front.
+ if (!rq.isQueueEmpty(Bucket.Transparent)) {
+ if (depthRangeChanged) {
+ renderer.setDepthRange(0, 1);
+ depthRangeChanged = false;
+ }
+
+ rq.renderQueue(Bucket.Transparent, this, cam, flush);
+ }
+
+ if (!rq.isQueueEmpty(Bucket.Gui)) {
+ renderer.setDepthRange(0, 0);
+ setCamera(cam, true);
+ rq.renderQueue(Bucket.Gui, this, cam, flush);
+ setCamera(cam, false);
+ depthRangeChanged = true;
+ }
+
+ // restore range to default
+ if (depthRangeChanged) {
+ renderer.setDepthRange(0, 1);
+ }
+ }
+
+ /**
+ * Renders the {@link Bucket#Translucent translucent queue} on the viewPort.
+ * <p>
+ * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) }
+ * is set to true. This method clears the translucent queue after rendering
+ * it.
+ *
+ * @param vp The viewport of which the translucent queue should be rendered.
+ *
+ * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean)
+ * @see #setHandleTranslucentBucket(boolean)
+ */
+ public void renderTranslucentQueue(ViewPort vp) {
+ RenderQueue rq = vp.getQueue();
+ if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {
+ rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);
+ }
+ }
+
+ private void setViewPort(Camera cam) {
+ // this will make sure to update viewport only if needed
+ if (cam != prevCam || cam.isViewportChanged()) {
+ viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
+ viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
+ viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
+ viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
+ renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
+ renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
+ cam.clearViewportChanged();
+ prevCam = cam;
+
+// float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);
+// float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);
+// float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);
+// float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);
+//
+// orthoMatrix.loadIdentity();
+// orthoMatrix.setTranslation(translateX, translateY, 0);
+// orthoMatrix.setScale(scaleX, scaleY, 0);
+
+ orthoMatrix.loadIdentity();
+ orthoMatrix.setTranslation(-1f, -1f, 0f);
+ orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);
+ }
+ }
+
+ private void setViewProjection(Camera cam, boolean ortho) {
+ if (shader) {
+ if (ortho) {
+ viewMatrix.set(Matrix4f.IDENTITY);
+ projMatrix.set(orthoMatrix);
+ viewProjMatrix.set(orthoMatrix);
+ } else {
+ viewMatrix.set(cam.getViewMatrix());
+ projMatrix.set(cam.getProjectionMatrix());
+ viewProjMatrix.set(cam.getViewProjectionMatrix());
+ }
+
+ camLoc.set(cam.getLocation());
+ cam.getLeft(camLeft);
+ cam.getUp(camUp);
+ cam.getDirection(camDir);
+
+ near = cam.getFrustumNear();
+ far = cam.getFrustumFar();
+ } else {
+ if (ortho) {
+ renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);
+ } else {
+ renderer.setViewProjectionMatrices(cam.getViewMatrix(),
+ cam.getProjectionMatrix());
+ }
+
+ }
+ }
+
+ /**
+ * Set the camera to use for rendering.
+ * <p>
+ * First, the camera's
+ * {@link Camera#setViewPort(float, float, float, float) view port parameters}
+ * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and
+ * {@link Camera#getProjectionMatrix() projection} matrices are set
+ * on the renderer. If <code>ortho</code> is <code>true</code>, then
+ * instead of using the camera's view and projection matrices, an ortho
+ * matrix is computed and used instead of the view projection matrix.
+ * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1)
+ * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1).
+ *
+ * @param cam The camera to set
+ * @param ortho True if to use orthographic projection (for GUI rendering),
+ * false if to use the camera's view and projection matrices.
+ */
+ public void setCamera(Camera cam, boolean ortho) {
+ setViewPort(cam);
+ setViewProjection(cam, ortho);
+ }
+
+ /**
+ * Draws the viewport but without notifying {@link SceneProcessor scene
+ * processors} of any rendering events.
+ *
+ * @param vp The ViewPort to render
+ *
+ * @see #renderViewPort(com.jme3.renderer.ViewPort, float)
+ */
+ public void renderViewPortRaw(ViewPort vp) {
+ setCamera(vp.getCamera(), false);
+ List<Spatial> scenes = vp.getScenes();
+ for (int i = scenes.size() - 1; i >= 0; i--) {
+ renderScene(scenes.get(i), vp);
+ }
+ flushQueue(vp);
+ }
+
+ /**
+ * Renders the {@link ViewPort}.
+ * <p>
+ * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method
+ * returns immediately. Otherwise, the ViewPort is rendered by
+ * the following process:<br>
+ * <ul>
+ * <li>All {@link SceneProcessor scene processors} that are attached
+ * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}.
+ * </li>
+ * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method
+ * is called.</li>
+ * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer}
+ * is set on the Renderer</li>
+ * <li>The camera is set on the renderer, including its view port parameters.
+ * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li>
+ * <li>Any buffers that the ViewPort requests to be cleared are cleared
+ * and the {@link ViewPort#getBackgroundColor() background color} is set</li>
+ * <li>Every scene that is attached to the ViewPort is flattened into
+ * the ViewPort's render queue
+ * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) })
+ * </li>
+ * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) }
+ * method is called.</li>
+ * <li>The render queue is sorted and then flushed, sending
+ * rendering commands to the underlying Renderer implementation.
+ * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li>
+ * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) }
+ * method is called.</li>
+ * <li>The translucent queue of the ViewPort is sorted and then flushed
+ * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
+ * <li>If any objects remained in the render queue, they are removed
+ * from the queue. This is generally objects added to the
+ * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)
+ * shadow queue}
+ * which were not rendered because of a missing shadow renderer.</li>
+ * </ul>
+ *
+ * @param vp
+ * @param tpf
+ */
+ public void renderViewPort(ViewPort vp, float tpf) {
+ if (!vp.isEnabled()) {
+ return;
+ }
+ List<SceneProcessor> processors = vp.getProcessors();
+ if (processors.isEmpty()) {
+ processors = null;
+ }
+
+ if (processors != null) {
+ for (SceneProcessor proc : processors) {
+ if (!proc.isInitialized()) {
+ proc.initialize(this, vp);
+ }
+ proc.preFrame(tpf);
+ }
+ }
+
+ renderer.setFrameBuffer(vp.getOutputFrameBuffer());
+ setCamera(vp.getCamera(), false);
+ if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {
+ if (vp.isClearColor()) {
+ renderer.setBackgroundColor(vp.getBackgroundColor());
+ }
+ renderer.clearBuffers(vp.isClearColor(),
+ vp.isClearDepth(),
+ vp.isClearStencil());
+ }
+
+ List<Spatial> scenes = vp.getScenes();
+ for (int i = scenes.size() - 1; i >= 0; i--) {
+ renderScene(scenes.get(i), vp);
+ }
+
+ if (processors != null) {
+ for (SceneProcessor proc : processors) {
+ proc.postQueue(vp.getQueue());
+ }
+ }
+
+ flushQueue(vp);
+
+ if (processors != null) {
+ for (SceneProcessor proc : processors) {
+ proc.postFrame(vp.getOutputFrameBuffer());
+ }
+ }
+ //renders the translucent objects queue after processors have been rendered
+ renderTranslucentQueue(vp);
+ // clear any remaining spatials that were not rendered.
+ clearQueue(vp);
+ }
+
+ /**
+ * Called by the application to render any ViewPorts
+ * added to this RenderManager.
+ * <p>
+ * Renders any viewports that were added using the following methods:
+ * <ul>
+ * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * </ul>
+ *
+ * @param tpf Time per frame value
+ */
+ public void render(float tpf, boolean mainFrameBufferActive) {
+ if (renderer instanceof NullRenderer) {
+ return;
+ }
+
+ this.shader = renderer.getCaps().contains(Caps.GLSL100);
+
+ for (int i = 0; i < preViewPorts.size(); i++) {
+ ViewPort vp = preViewPorts.get(i);
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
+ renderViewPort(vp, tpf);
+ }
+ }
+ for (int i = 0; i < viewPorts.size(); i++) {
+ ViewPort vp = viewPorts.get(i);
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
+ renderViewPort(vp, tpf);
+ }
+ }
+ for (int i = 0; i < postViewPorts.size(); i++) {
+ ViewPort vp = postViewPorts.get(i);
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
+ renderViewPort(vp, tpf);
+ }
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/Renderer.java b/engine/src/core/com/jme3/renderer/Renderer.java
new file mode 100644
index 0000000..6e366ab
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/Renderer.java
@@ -0,0 +1,305 @@
+/*
+ * 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.light.LightList;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.shader.Shader;
+import com.jme3.shader.Shader.ShaderSource;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import java.nio.ByteBuffer;
+import java.util.EnumSet;
+
+/**
+ * The <code>Renderer</code> is responsible for taking rendering commands and
+ * executing them on the underlying video hardware.
+ *
+ * @author Kirill Vainer
+ */
+public interface Renderer {
+
+ /**
+ * Get the capabilities of the renderer.
+ * @return The capabilities of the renderer.
+ */
+ public EnumSet<Caps> getCaps();
+
+ /**
+ * The statistics allow tracking of how data
+ * per frame, such as number of objects rendered, number of triangles, etc.
+ * These are updated when the Renderer's methods are used, make sure
+ * to call {@link Statistics#clearFrame() } at the appropriate time
+ * to get accurate info per frame.
+ */
+ public Statistics getStatistics();
+
+ /**
+ * Invalidates the current rendering state. Should be called after
+ * the GL state was changed manually or through an external library.
+ */
+ public void invalidateState();
+
+ /**
+ * Clears certain channels of the currently bound framebuffer.
+ *
+ * @param color True if to clear colors (RGBA)
+ * @param depth True if to clear depth/z
+ * @param stencil True if to clear stencil buffer (if available, otherwise
+ * ignored)
+ */
+ public void clearBuffers(boolean color, boolean depth, boolean stencil);
+
+ /**
+ * Sets the background (aka clear) color.
+ *
+ * @param color The background color to set
+ */
+ public void setBackgroundColor(ColorRGBA color);
+
+ /**
+ * Applies the given {@link RenderState}, making the necessary
+ * GL calls so that the state is applied.
+ */
+ public void applyRenderState(RenderState state);
+
+ /**
+ * Set the range of the depth values for objects. All rendered
+ * objects will have their depth clamped to this range.
+ *
+ * @param start The range start
+ * @param end The range end
+ */
+ public void setDepthRange(float start, float end);
+
+ /**
+ * Called when a new frame has been rendered.
+ */
+ public void onFrame();
+
+ /**
+ * Set the world matrix to use. Does nothing if the Renderer is
+ * shader based.
+ *
+ * @param worldMatrix World matrix to use.
+ */
+ public void setWorldMatrix(Matrix4f worldMatrix);
+
+ /**
+ * Sets the view and projection matrices to use. Does nothing if the Renderer
+ * is shader based.
+ *
+ * @param viewMatrix The view matrix to use.
+ * @param projMatrix The projection matrix to use.
+ */
+ public void setViewProjectionMatrices(Matrix4f viewMatrix, Matrix4f projMatrix);
+
+ /**
+ * Set the viewport location and resolution on the screen.
+ *
+ * @param x The x coordinate of the viewport
+ * @param y The y coordinate of the viewport
+ * @param width Width of the viewport
+ * @param height Height of the viewport
+ */
+ public void setViewPort(int x, int y, int width, int height);
+
+ /**
+ * Specifies a clipping rectangle.
+ * For all future rendering commands, no pixels will be allowed
+ * to be rendered outside of the clip rectangle.
+ *
+ * @param x The x coordinate of the clip rect
+ * @param y The y coordinate of the clip rect
+ * @param width Width of the clip rect
+ * @param height Height of the clip rect
+ */
+ public void setClipRect(int x, int y, int width, int height);
+
+ /**
+ * Clears the clipping rectangle set with
+ * {@link #setClipRect(int, int, int, int) }.
+ */
+ public void clearClipRect();
+
+ /**
+ * Set lighting state.
+ * Does nothing if the renderer is shader based.
+ * The lights should be provided in world space.
+ * Specify <code>null</code> to disable lighting.
+ *
+ * @param lights The light list to set.
+ */
+ public void setLighting(LightList lights);
+
+ /**
+ * Sets the shader to use for rendering.
+ * If the shader has not been uploaded yet, it is compiled
+ * and linked. If it has been uploaded, then the
+ * uniform data is updated and the shader is set.
+ *
+ * @param shader The shader to use for rendering.
+ */
+ public void setShader(Shader shader);
+
+ /**
+ * Deletes a shader. This method also deletes
+ * the attached shader sources.
+ *
+ * @param shader Shader to delete.
+ */
+ public void deleteShader(Shader shader);
+
+ /**
+ * Deletes the provided shader source.
+ *
+ * @param source The ShaderSource to delete.
+ */
+ public void deleteShaderSource(ShaderSource source);
+
+ /**
+ * Copies contents from src to dst, scaling if necessary.
+ */
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst);
+
+ /**
+ * Copies contents from src to dst, scaling if necessary.
+ * set copyDepth to false to only copy the color buffers.
+ */
+ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth);
+
+ /**
+ * Sets the framebuffer that will be drawn to.
+ */
+ public void setFrameBuffer(FrameBuffer fb);
+
+ /**
+ * Set the framebuffer that will be set instead of the main framebuffer
+ * when a call to setFrameBuffer(null) is made.
+ *
+ * @param fb
+ */
+ public void setMainFrameBufferOverride(FrameBuffer fb);
+
+ /**
+ * Reads the pixels currently stored in the specified framebuffer
+ * into the given ByteBuffer object.
+ * Only color pixels are transferred, the format is BGRA with 8 bits
+ * per component. The given byte buffer should have at least
+ * fb.getWidth() * fb.getHeight() * 4 bytes remaining.
+ *
+ * @param fb The framebuffer to read from
+ * @param byteBuf The bytebuffer to transfer color data to
+ */
+ public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf);
+
+ /**
+ * Deletes a framebuffer and all attached renderbuffers
+ */
+ public void deleteFrameBuffer(FrameBuffer fb);
+
+ /**
+ * Sets the texture to use for the given texture unit.
+ */
+ public void setTexture(int unit, Texture tex);
+
+ /**
+ * Deletes a texture from the GPU.
+ */
+ public void deleteImage(Image image);
+
+ /**
+ * Uploads a vertex buffer to the GPU.
+ *
+ * @param vb The vertex buffer to upload
+ */
+ public void updateBufferData(VertexBuffer vb);
+
+ /**
+ * Deletes a vertex buffer from the GPU.
+ * @param vb The vertex buffer to delete
+ */
+ public void deleteBuffer(VertexBuffer vb);
+
+ /**
+ * Renders <code>count</code> meshes, with the geometry data supplied.
+ * The shader which is currently set with <code>setShader</code> is
+ * responsible for transforming the input verticies into clip space
+ * and shading it based on the given vertex attributes.
+ * The int variable gl_InstanceID can be used to access the current
+ * instance of the mesh being rendered inside the vertex shader.
+ *
+ * @param mesh The mesh to render
+ * @param lod The LOD level to use, see {@link Mesh#setLodLevels(com.jme3.scene.VertexBuffer[]) }.
+ * @param count Number of mesh instances to render
+ */
+ public void renderMesh(Mesh mesh, int lod, int count);
+
+ /**
+ * Resets all previously used {@link GLObject}s on this Renderer.
+ * The state of the GLObjects is reset in such way, that using
+ * them again will cause the renderer to reupload them.
+ * Call this method when you know the GL context is going to shutdown.
+ *
+ * @see GLObject#resetObject()
+ */
+ public void resetGLObjects();
+
+ /**
+ * Deletes all previously used {@link GLObject}s on this Renderer, and
+ * then resets the GLObjects.
+ *
+ * @see #resetGLObjects()
+ * @see GLObject#deleteObject(com.jme3.renderer.Renderer)
+ */
+ public void cleanup();
+
+ /**
+ * Sets the alpha to coverage state.
+ * <p>
+ * When alpha coverage and multi-sampling is enabled,
+ * each pixel will contain alpha coverage in all
+ * of its subsamples, which is then combined when
+ * other future alpha-blended objects are rendered.
+ * </p>
+ * <p>
+ * Alpha-to-coverage is useful for rendering transparent objects
+ * without having to worry about sorting them.
+ * </p>
+ */
+ public void setAlphaToCoverage(boolean value);
+}
diff --git a/engine/src/core/com/jme3/renderer/RendererException.java b/engine/src/core/com/jme3/renderer/RendererException.java
new file mode 100644
index 0000000..41964a2
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/RendererException.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/**
+ * <code>RendererException</code> is raised when a renderer encounters
+ * a fatal rendering error.
+ *
+ * @author Kirill Vainer
+ */
+public class RendererException extends RuntimeException {
+
+ /**
+ * Creates a new instance of <code>RendererException</code>
+ */
+ public RendererException(String message){
+ super(message);
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/Statistics.java b/engine/src/core/com/jme3/renderer/Statistics.java
new file mode 100644
index 0000000..fb8f2a0
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/Statistics.java
@@ -0,0 +1,255 @@
+/*
+ * 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.scene.Mesh;
+import com.jme3.shader.Shader;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+import java.util.HashSet;
+
+/**
+ * The statistics class allows tracking of real-time rendering statistics.
+ * <p>
+ * The <code>Statistics</code> can be retrieved by using {@link Renderer#getStatistics() }.
+ *
+ * @author Kirill Vainer
+ */
+public class Statistics {
+
+ protected int numObjects;
+ protected int numTriangles;
+ protected int numVertices;
+ protected int numShaderSwitches;
+ protected int numTextureBinds;
+ protected int numFboSwitches;
+ protected int numUniformsSet;
+
+ protected int memoryShaders;
+ protected int memoryFrameBuffers;
+ protected int memoryTextures;
+
+ protected HashSet<Integer> shadersUsed = new HashSet<Integer>();
+ protected HashSet<Integer> texturesUsed = new HashSet<Integer>();
+ protected HashSet<Integer> fbosUsed = new HashSet<Integer>();
+
+ /**
+ * Returns a list of labels corresponding to each statistic.
+ *
+ * @return a list of labels corresponding to each statistic.
+ *
+ * @see #getData(int[])
+ */
+ public String[] getLabels(){
+ return new String[]{ "Vertices",
+ "Triangles",
+ "Uniforms",
+
+ "Objects",
+
+ "Shaders (S)",
+ "Shaders (F)",
+ "Shaders (M)",
+
+ "Textures (S)",
+ "Textures (F)",
+ "Textures (M)",
+
+ "FrameBuffers (S)",
+ "FrameBuffers (F)",
+ "FrameBuffers (M)" };
+
+ }
+
+ /**
+ * Retrieves the statistics data into the given array.
+ * The array should be as large as the array given in
+ * {@link #getLabels() }.
+ *
+ * @param data The data array to write to
+ */
+ public void getData(int[] data){
+ data[0] = numVertices;
+ data[1] = numTriangles;
+ data[2] = numUniformsSet;
+ data[3] = numObjects;
+
+ data[4] = numShaderSwitches;
+ data[5] = shadersUsed.size();
+ data[6] = memoryShaders;
+
+ data[7] = numTextureBinds;
+ data[8] = texturesUsed.size();
+ data[9] = memoryTextures;
+
+ data[10] = numFboSwitches;
+ data[11] = fbosUsed.size();
+ data[12] = memoryFrameBuffers;
+ }
+
+ /**
+ * Called by the Renderer when a mesh has been drawn.
+ *
+ */
+ public void onMeshDrawn(Mesh mesh, int lod){
+ numObjects ++;
+ numTriangles += mesh.getTriangleCount(lod);
+ numVertices += mesh.getVertexCount();
+ }
+
+ /**
+ * Called by the Renderer when a shader has been utilized.
+ *
+ * @param shader The shader that was used
+ * @param wasSwitched If true, the shader has required a state switch
+ */
+ public void onShaderUse(Shader shader, boolean wasSwitched){
+ assert shader.getId() >= 1;
+
+ if (!shadersUsed.contains(shader.getId()))
+ shadersUsed.add(shader.getId());
+
+ if (wasSwitched)
+ numShaderSwitches++;
+ }
+
+ /**
+ * Called by the Renderer when a uniform was set.
+ */
+ public void onUniformSet(){
+ numUniformsSet ++;
+ }
+
+ /**
+ * Called by the Renderer when a texture has been set.
+ *
+ * @param image The image that was set
+ * @param wasSwitched If true, the texture has required a state switch
+ */
+ public void onTextureUse(Image image, boolean wasSwitched){
+ assert image.getId() >= 1;
+
+ if (!texturesUsed.contains(image.getId()))
+ texturesUsed.add(image.getId());
+
+ if (wasSwitched)
+ numTextureBinds ++;
+ }
+
+ /**
+ * Called by the Renderer when a framebuffer has been set.
+ *
+ * @param fb The framebuffer that was set
+ * @param wasSwitched If true, the framebuffer required a state switch
+ */
+ public void onFrameBufferUse(FrameBuffer fb, boolean wasSwitched){
+ if (fb != null){
+ assert fb.getId() >= 1;
+
+ if (!fbosUsed.contains(fb.getId()))
+ fbosUsed.add(fb.getId());
+ }
+
+ if (wasSwitched)
+ numFboSwitches ++;
+ }
+
+ /**
+ * Clears all frame-specific statistics such as objects used per frame.
+ */
+ public void clearFrame(){
+ shadersUsed.clear();
+ texturesUsed.clear();
+ fbosUsed.clear();
+
+ numObjects = 0;
+ numTriangles = 0;
+ numVertices = 0;
+ numShaderSwitches = 0;
+ numTextureBinds = 0;
+ numFboSwitches = 0;
+ numUniformsSet = 0;
+ }
+
+ /**
+ * Called by the Renderer when it creates a new shader
+ */
+ public void onNewShader(){
+ memoryShaders ++;
+ }
+
+ /**
+ * Called by the Renderer when it creates a new texture
+ */
+ public void onNewTexture(){
+ memoryTextures ++;
+ }
+
+ /**
+ * Called by the Renderer when it creates a new framebuffer
+ */
+ public void onNewFrameBuffer(){
+ memoryFrameBuffers ++;
+ }
+
+ /**
+ * Called by the Renderer when it deletes a shader
+ */
+ public void onDeleteShader(){
+ memoryShaders --;
+ }
+
+ /**
+ * Called by the Renderer when it deletes a texture
+ */
+ public void onDeleteTexture(){
+ memoryTextures --;
+ }
+
+ /**
+ * Called by the Renderer when it deletes a framebuffer
+ */
+ public void onDeleteFrameBuffer(){
+ memoryFrameBuffers --;
+ }
+
+ /**
+ * Called when video memory is cleared.
+ */
+ public void clearMemory(){
+ memoryFrameBuffers = 0;
+ memoryShaders = 0;
+ memoryTextures = 0;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/ViewPort.java b/engine/src/core/com/jme3/renderer/ViewPort.java
new file mode 100644
index 0000000..ad9ab2f
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/ViewPort.java
@@ -0,0 +1,363 @@
+/*
+ * 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.renderer;
+
+import com.jme3.math.ColorRGBA;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Spatial;
+import com.jme3.texture.FrameBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A <code>ViewPort</code> represents a view inside the display
+ * window or a {@link FrameBuffer} to which scenes will be rendered.
+ * <p>
+ * A viewport has a {@link #ViewPort(java.lang.String, com.jme3.renderer.Camera) camera}
+ * which is used to render a set of {@link #attachScene(com.jme3.scene.Spatial) scenes}.
+ * A view port has a location on the screen as set by the
+ * {@link Camera#setViewPort(float, float, float, float) } method.
+ * By default, a view port does not clear the framebuffer, but it can be
+ * set to {@link #setClearFlags(boolean, boolean, boolean) clear the framebuffer}.
+ * The background color which the color buffer is cleared to can be specified
+ * via the {@link #setBackgroundColor(com.jme3.math.ColorRGBA)} method.
+ * <p>
+ * A ViewPort has a list of {@link SceneProcessor}s which can
+ * control how the ViewPort is rendered by the {@link RenderManager}.
+ *
+ * @author Kirill Vainer
+ *
+ * @see RenderManager
+ * @see SceneProcessor
+ * @see Spatial
+ * @see Camera
+ */
+public class ViewPort {
+
+ protected final String name;
+ protected final Camera cam;
+ protected final RenderQueue queue = new RenderQueue();
+ protected final ArrayList<Spatial> sceneList = new ArrayList<Spatial>();
+ protected final ArrayList<SceneProcessor> processors = new ArrayList<SceneProcessor>();
+ protected FrameBuffer out = null;
+
+ protected final ColorRGBA backColor = new ColorRGBA(0,0,0,0);
+ protected boolean clearDepth = false, clearColor = false, clearStencil = false;
+ private boolean enabled = true;
+
+ /**
+ * Create a new viewport. User code should generally use these methods instead:<br>
+ * <ul>
+ * <li>{@link RenderManager#createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * <li>{@link RenderManager#createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * <li>{@link RenderManager#createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>
+ * </ul>
+ *
+ * @param name The name of the viewport. Used for debugging only.
+ * @param cam The camera through which the viewport is rendered. The camera
+ * cannot be swapped to a different one after creating the viewport.
+ */
+ public ViewPort(String name, Camera cam) {
+ this.name = name;
+ this.cam = cam;
+ }
+
+ /**
+ * Returns the name of the viewport as set in the constructor.
+ *
+ * @return the name of the viewport
+ *
+ * @see #ViewPort(java.lang.String, com.jme3.renderer.Camera)
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the list of {@link SceneProcessor scene processors} that were
+ * added to this <code>ViewPort</code>
+ *
+ * @return the list of processors attached to this ViewPort
+ *
+ * @see #addProcessor(com.jme3.post.SceneProcessor)
+ */
+ public List<SceneProcessor> getProcessors(){
+ return processors;
+ }
+
+ /**
+ * Adds a {@link SceneProcessor} to this ViewPort.
+ * <p>
+ * SceneProcessors that are added to the ViewPort will be notified
+ * of events as the ViewPort is being rendered by the {@link RenderManager}.
+ *
+ * @param processor The processor to add
+ *
+ * @see SceneProcessor
+ */
+ public void addProcessor(SceneProcessor processor){
+ processors.add(processor);
+ }
+
+ /**
+ * Removes a {@link SceneProcessor} from this ViewPort.
+ * <p>
+ * The processor will no longer receive events occurring to this ViewPort.
+ *
+ * @param processor The processor to remove
+ *
+ * @see SceneProcessor
+ */
+ public void removeProcessor(SceneProcessor processor){
+ processors.remove(processor);
+ processor.cleanup();
+ }
+
+ /**
+ * Check if depth buffer clearing is enabled.
+ *
+ * @return true if depth buffer clearing is enabled.
+ *
+ * @see #setClearDepth(boolean)
+ */
+ public boolean isClearDepth() {
+ return clearDepth;
+ }
+
+ /**
+ * Enable or disable clearing of the depth buffer for this ViewPort.
+ * <p>
+ * By default depth clearing is disabled.
+ *
+ * @param clearDepth Enable/disable depth buffer clearing.
+ */
+ public void setClearDepth(boolean clearDepth) {
+ this.clearDepth = clearDepth;
+ }
+
+ /**
+ * Check if color buffer clearing is enabled.
+ *
+ * @return true if color buffer clearing is enabled.
+ *
+ * @see #setClearColor(boolean)
+ */
+ public boolean isClearColor() {
+ return clearColor;
+ }
+
+ /**
+ * Enable or disable clearing of the color buffer for this ViewPort.
+ * <p>
+ * By default color clearing is disabled.
+ *
+ * @param clearColor Enable/disable color buffer clearing.
+ */
+ public void setClearColor(boolean clearColor) {
+ this.clearColor = clearColor;
+ }
+
+ /**
+ * Check if stencil buffer clearing is enabled.
+ *
+ * @return true if stencil buffer clearing is enabled.
+ *
+ * @see #setClearStencil(boolean)
+ */
+ public boolean isClearStencil() {
+ return clearStencil;
+ }
+
+ /**
+ * Enable or disable clearing of the stencil buffer for this ViewPort.
+ * <p>
+ * By default stencil clearing is disabled.
+ *
+ * @param clearStencil Enable/disable stencil buffer clearing.
+ */
+ public void setClearStencil(boolean clearStencil) {
+ this.clearStencil = clearStencil;
+ }
+
+ /**
+ * Set the clear flags (color, depth, stencil) in one call.
+ *
+ * @param color If color buffer clearing should be enabled.
+ * @param depth If depth buffer clearing should be enabled.
+ * @param stencil If stencil buffer clearing should be enabled.
+ *
+ * @see #setClearColor(boolean)
+ * @see #setClearDepth(boolean)
+ * @see #setClearStencil(boolean)
+ */
+ public void setClearFlags(boolean color, boolean depth, boolean stencil){
+ this.clearColor = color;
+ this.clearDepth = depth;
+ this.clearStencil = stencil;
+ }
+
+ /**
+ * Returns the framebuffer where this ViewPort's scenes are
+ * rendered to.
+ *
+ * @return the framebuffer where this ViewPort's scenes are
+ * rendered to.
+ *
+ * @see #setOutputFrameBuffer(com.jme3.texture.FrameBuffer)
+ */
+ public FrameBuffer getOutputFrameBuffer() {
+ return out;
+ }
+
+ /**
+ * Sets the output framebuffer for the ViewPort.
+ * <p>
+ * The output framebuffer specifies where the scenes attached
+ * to this ViewPort are rendered to. By default this is <code>null</code>
+ * which indicates the scenes are rendered to the display window.
+ *
+ * @param out The framebuffer to render scenes to, or null if to render
+ * to the screen.
+ */
+ public void setOutputFrameBuffer(FrameBuffer out) {
+ this.out = out;
+ }
+
+ /**
+ * Returns the camera which renders the attached scenes.
+ *
+ * @return the camera which renders the attached scenes.
+ *
+ * @see Camera
+ */
+ public Camera getCamera() {
+ return cam;
+ }
+
+ /**
+ * Internal use only.
+ */
+ public RenderQueue getQueue() {
+ return queue;
+ }
+
+ /**
+ * Attaches a new scene to render in this ViewPort.
+ *
+ * @param scene The scene to attach
+ *
+ * @see Spatial
+ */
+ public void attachScene(Spatial scene){
+ sceneList.add(scene);
+ }
+
+ /**
+ * Detaches a scene from rendering.
+ *
+ * @param scene The scene to detach
+ *
+ * @see #attachScene(com.jme3.scene.Spatial)
+ */
+ public void detachScene(Spatial scene){
+ sceneList.remove(scene);
+ }
+
+ /**
+ * Removes all attached scenes.
+ *
+ * @see #attachScene(com.jme3.scene.Spatial)
+ */
+ public void clearScenes() {
+ sceneList.clear();
+ }
+
+ /**
+ * Returns a list of all attached scenes.
+ *
+ * @return a list of all attached scenes.
+ *
+ * @see #attachScene(com.jme3.scene.Spatial)
+ */
+ public List<Spatial> getScenes(){
+ return sceneList;
+ }
+
+ /**
+ * Sets the background color.
+ * <p>
+ * When the ViewPort's color buffer is cleared
+ * (if {@link #setClearColor(boolean) color clearing} is enabled),
+ * this specifies the color to which the color buffer is set to.
+ * By default the background color is black without alpha.
+ *
+ * @param background the background color.
+ */
+ public void setBackgroundColor(ColorRGBA background){
+ backColor.set(background);
+ }
+
+ /**
+ * Returns the background color of this ViewPort
+ *
+ * @return the background color of this ViewPort
+ *
+ * @see #setBackgroundColor(com.jme3.math.ColorRGBA)
+ */
+ public ColorRGBA getBackgroundColor(){
+ return backColor;
+ }
+
+ /**
+ * Enable or disable this ViewPort.
+ * <p>
+ * Disabled ViewPorts are skipped by the {@link RenderManager} when
+ * rendering. By default all ViewPorts are enabled.
+ *
+ * @param enable If the viewport should be disabled or enabled.
+ */
+ public void setEnabled(boolean enable) {
+ this.enabled = enable;
+ }
+
+ /**
+ * Returns true if the viewport is enabled, false otherwise.
+ * @return true if the viewport is enabled, false otherwise.
+ * @see #setEnabled(boolean)
+ */
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/package.html b/engine/src/core/com/jme3/renderer/package.html
new file mode 100644
index 0000000..9d80ec8
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/package.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.renderer</code> package provides classes responsible for
+rendering.
+<p>
+The most critical classes are the {@link com.jme3.renderer.Renderer},
+which is the low-level rendering implementation and is abstract, and the
+{@link com.jme3.renderer.RenderManager} class, which provides the high-level
+rendering logic on top of the <code>Renderer</code>.
+<p>
+To accompany rendering, several helper classes are available.
+<ul>
+ <li>The {@link com.jme3.renderer.Camera} is used to specify the point-of-view
+ from which scenes are rendered.</li>
+ <li>The {@link com.jme3.renderer.ViewPort} is the
+aggregation of a Camera and a set of {@link com.jme3.scene.Spatial scenes}
+which are to be rendered, as well as additional info.</li>
+ <li>The {@link com.jme3.renderer.Caps} class contains renderer capabilities
+which the user can query to find out what features are available in the
+rendering implementation. </li>
+ <li>The {@link com.jme3.renderer.Statistics} class is updated in real time
+ by the Renderer, and is used to find out various statistics about
+ the rendering</li>
+ <li>The {@link com.jme3.renderer.GLObjectManager} and {@link com.jme3.renderer.GLObject} classes
+ provide a link between the renderer's native objects and Java's garbage collected objects,
+ allowing the engine to track when the Java object counterpart is garbage collected
+ and then delete the native object counterpart from the renderer.</li>
+</ul>
+
+</body>
+</html>
diff --git a/engine/src/core/com/jme3/renderer/queue/GeometryComparator.java b/engine/src/core/com/jme3/renderer/queue/GeometryComparator.java
new file mode 100644
index 0000000..8f42df7
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/GeometryComparator.java
@@ -0,0 +1,53 @@
+/*
+ * 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.queue;
+
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+import java.util.Comparator;
+
+/**
+ * <code>GeometryComparator</code> is a special version of {@link Comparator}
+ * that is used to sort geometries for rendering in the {@link RenderQueue}.
+ *
+ * @author Kirill Vainer
+ */
+public interface GeometryComparator extends Comparator<Geometry> {
+
+ /**
+ * Set the camera to use for sorting.
+ *
+ * @param cam The camera to use for sorting
+ */
+ public void setCamera(Camera cam);
+}
diff --git a/engine/src/core/com/jme3/renderer/queue/GeometryList.java b/engine/src/core/com/jme3/renderer/queue/GeometryList.java
new file mode 100644
index 0000000..9f36ed9
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/GeometryList.java
@@ -0,0 +1,143 @@
+/*
+ * 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.queue;
+
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+import com.jme3.util.SortUtil;
+
+/**
+ * This class is a special purpose list of {@link Geometry} objects for render
+ * queuing.
+ *
+ * @author Jack Lindamood
+ * @author Three Rings - better sorting alg.
+ * @author Kirill Vainer
+ */
+public class GeometryList {
+
+ private static final int DEFAULT_SIZE = 32;
+
+ private Geometry[] geometries;
+ private Geometry[] geometries2;
+ private int size;
+ private GeometryComparator comparator;
+
+ /**
+ * Initializes the GeometryList to use the given {@link GeometryComparator}
+ * to use for comparing geometries.
+ *
+ * @param comparator The comparator to use.
+ */
+ public GeometryList(GeometryComparator comparator) {
+ size = 0;
+ geometries = new Geometry[DEFAULT_SIZE];
+ geometries2 = new Geometry[DEFAULT_SIZE];
+ this.comparator = comparator;
+ }
+
+ /**
+ * Set the camera that will be set on the geometry comparators
+ * via {@link GeometryComparator#setCamera(com.jme3.renderer.Camera)}.
+ *
+ * @param cam Camera to use for sorting.
+ */
+ public void setCamera(Camera cam){
+ this.comparator.setCamera(cam);
+ }
+
+ /**
+ * Returns the number of elements in this GeometryList.
+ *
+ * @return Number of elements in the list
+ */
+ public int size(){
+ return size;
+ }
+
+ /**
+ * Returns the element at the given index.
+ *
+ * @param index The index to lookup
+ * @return Geometry at the index
+ */
+ public Geometry get(int index){
+ return geometries[index];
+ }
+
+ /**
+ * Adds a geometry to the list.
+ * List size is doubled if there is no room.
+ *
+ * @param g
+ * The geometry to add.
+ */
+ public void add(Geometry g) {
+ if (size == geometries.length) {
+ Geometry[] temp = new Geometry[size * 2];
+ System.arraycopy(geometries, 0, temp, 0, size);
+ geometries = temp; // original list replaced by double-size list
+
+ geometries2 = new Geometry[size * 2];
+ }
+ geometries[size++] = g;
+ }
+
+ /**
+ * Resets list size to 0.
+ */
+ public void clear() {
+ for (int i = 0; i < size; i++){
+ geometries[i] = null;
+ }
+
+ size = 0;
+ }
+
+ /**
+ * Sorts the elements in the list according to their Comparator.
+ */
+ public void sort() {
+ if (size > 1) {
+ // sort the spatial list using the comparator
+
+// SortUtil.qsort(geometries, 0, size, comparator);
+// Arrays.sort(geometries, 0, size, comparator);
+
+ System.arraycopy(geometries, 0, geometries2, 0, size);
+ SortUtil.msort(geometries2, geometries, 0, size-1, comparator);
+
+
+ }
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/renderer/queue/GuiComparator.java b/engine/src/core/com/jme3/renderer/queue/GuiComparator.java
new file mode 100644
index 0000000..b35a03c
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/GuiComparator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.queue;
+
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+
+/**
+ * <code>GuiComparator</code> sorts geometries back-to-front based
+ * on their Z position.
+ *
+ * @author Kirill Vainer
+ */
+public class GuiComparator implements GeometryComparator {
+
+ public int compare(Geometry o1, Geometry o2) {
+ float z1 = o1.getWorldTranslation().getZ();
+ float z2 = o2.getWorldTranslation().getZ();
+ if (z1 > z2)
+ return 1;
+ else if (z1 < z2)
+ return -1;
+ else
+ return 0;
+ }
+
+ public void setCamera(Camera cam) {
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/queue/NullComparator.java b/engine/src/core/com/jme3/renderer/queue/NullComparator.java
new file mode 100644
index 0000000..6463558
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/NullComparator.java
@@ -0,0 +1,51 @@
+/*
+ * 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.queue;
+
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+
+/**
+ * <code>NullComparator</code> does not sort geometries. They will be in
+ * arbitrary order.
+ *
+ * @author Kirill Vainer
+ */
+public class NullComparator implements GeometryComparator {
+ public int compare(Geometry o1, Geometry o2) {
+ return 0;
+ }
+
+ public void setCamera(Camera cam) {
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/queue/OpaqueComparator.java b/engine/src/core/com/jme3/renderer/queue/OpaqueComparator.java
new file mode 100644
index 0000000..85f4093
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/OpaqueComparator.java
@@ -0,0 +1,96 @@
+/*
+ * 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.queue;
+
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+
+public class OpaqueComparator implements GeometryComparator {
+
+ private Camera cam;
+ private final Vector3f tempVec = new Vector3f();
+ private final Vector3f tempVec2 = new Vector3f();
+
+ public void setCamera(Camera cam){
+ this.cam = cam;
+ }
+
+ public float distanceToCam(Geometry spat){
+ if (spat == null)
+ return Float.NEGATIVE_INFINITY;
+
+ if (spat.queueDistance != Float.NEGATIVE_INFINITY)
+ return spat.queueDistance;
+
+ Vector3f camPosition = cam.getLocation();
+ Vector3f viewVector = cam.getDirection(tempVec2);
+ Vector3f spatPosition = null;
+
+ if (spat.getWorldBound() != null){
+ spatPosition = spat.getWorldBound().getCenter();
+ }else{
+ spatPosition = spat.getWorldTranslation();
+ }
+
+ spatPosition.subtract(camPosition, tempVec);
+ spat.queueDistance = tempVec.dot(viewVector);
+
+ return spat.queueDistance;
+ }
+
+ public int compare(Geometry o1, Geometry o2) {
+ Material m1 = o1.getMaterial();
+ Material m2 = o2.getMaterial();
+
+ int sortId = m1.compareTo(m2);
+
+ if (sortId == 0){
+ // use the same shader.
+ // sort front-to-back then.
+ float d1 = distanceToCam(o1);
+ float d2 = distanceToCam(o2);
+
+ if (d1 == d2)
+ return 0;
+ else if (d1 < d2)
+ return -1;
+ else
+ return 1;
+ }else{
+ return sortId;
+ }
+ }
+
+}
diff --git a/engine/src/core/com/jme3/renderer/queue/RenderQueue.java b/engine/src/core/com/jme3/renderer/queue/RenderQueue.java
new file mode 100644
index 0000000..dba604e
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/RenderQueue.java
@@ -0,0 +1,377 @@
+/*
+ * 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.queue;
+
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Spatial;
+
+/**
+ * <code>RenderQueue</code> is used to queue up and sort
+ * {@link Geometry geometries} for rendering.
+ *
+ * @author Kirill Vainer
+ */
+public class RenderQueue {
+
+ private GeometryList opaqueList;
+ private GeometryList guiList;
+ private GeometryList transparentList;
+ private GeometryList translucentList;
+ private GeometryList skyList;
+ private GeometryList shadowRecv;
+ private GeometryList shadowCast;
+
+ /**
+ * Creates a new RenderQueue, the default {@link GeometryComparator comparators}
+ * are used for all {@link GeometryList geometry lists}.
+ */
+ public RenderQueue() {
+ this.opaqueList = new GeometryList(new OpaqueComparator());
+ this.guiList = new GeometryList(new GuiComparator());
+ this.transparentList = new GeometryList(new TransparentComparator());
+ this.translucentList = new GeometryList(new TransparentComparator());
+ this.skyList = new GeometryList(new NullComparator());
+ this.shadowRecv = new GeometryList(new OpaqueComparator());
+ this.shadowCast = new GeometryList(new OpaqueComparator());
+ }
+
+ /**
+ * The render queue <code>Bucket</code> specifies the bucket
+ * to which the spatial will be placed when rendered.
+ * <p>
+ * The behavior of the rendering will differ depending on which
+ * bucket the spatial is placed. A spatial's queue bucket can be set
+ * via {@link Spatial#setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket) }.
+ */
+ public enum Bucket {
+ /**
+ * The renderer will try to find the optimal order for rendering all
+ * objects using this mode.
+ * You should use this mode for most normal objects, except transparent
+ * ones, as it could give a nice performance boost to your application.
+ */
+ Opaque,
+
+ /**
+ * This is the mode you should use for object with
+ * transparency in them. It will ensure the objects furthest away are
+ * rendered first. That ensures when another transparent object is drawn on
+ * top of previously drawn objects, you can see those (and the object drawn
+ * using Opaque) through the transparent parts of the newly drawn
+ * object.
+ */
+ Transparent,
+
+ /**
+ * A special mode used for rendering really far away, flat objects -
+ * e.g. skies. In this mode, the depth is set to infinity so
+ * spatials in this bucket will appear behind everything, the downside
+ * to this bucket is that 3D objects will not be rendered correctly
+ * due to lack of depth testing.
+ */
+ Sky,
+
+ /**
+ * A special mode used for rendering transparent objects that
+ * should not be effected by {@link SceneProcessor}.
+ * Generally this would contain translucent objects, and
+ * also objects that do not write to the depth buffer such as
+ * particle emitters.
+ */
+ Translucent,
+
+ /**
+ * This is a special mode, for drawing 2D object
+ * without perspective (such as GUI or HUD parts).
+ * The spatial's world coordinate system has the range
+ * of [0, 0, -1] to [Width, Height, 1] where Width/Height is
+ * the resolution of the screen rendered to. Any spatials
+ * outside of that range are culled.
+ */
+ Gui,
+
+ /**
+ * A special mode, that will ensure that this spatial uses the same
+ * mode as the parent Node does.
+ */
+ Inherit,
+ }
+
+ /**
+ * <code>ShadowMode</code> is a marker used to specify how shadow
+ * effects should treat the spatial.
+ */
+ public enum ShadowMode {
+ /**
+ * Disable both shadow casting and shadow receiving for this spatial.
+ * Generally used for special effects like particle emitters.
+ */
+ Off,
+
+ /**
+ * Enable casting of shadows but not receiving them.
+ */
+ Cast,
+
+ /**
+ * Enable receiving of shadows but not casting them.
+ */
+ Receive,
+
+ /**
+ * Enable both receiving and casting of shadows.
+ */
+ CastAndReceive,
+
+ /**
+ * Inherit the <code>ShadowMode</code> from the parent node.
+ */
+ Inherit
+ }
+
+ /**
+ * Sets a different geometry comparator for the specified bucket, one
+ * of Gui, Opaque, Sky, or Transparent. The GeometryComparators are
+ * used to sort the accumulated list of geometries before actual rendering
+ * occurs.
+ *
+ * <p>The most significant comparator is the one for the transparent
+ * bucket since there is no correct way to sort the transparent bucket
+ * that will handle all geometry all the time. In certain cases, the
+ * application may know the best way to sort and now has the option of
+ * configuring a specific implementation.</p>
+ *
+ * <p>The default comparators are:</p>
+ * <ul>
+ * <li>Bucket.Opaque: {@link com.jme3.renderer.queue.OpaqueComparator} which sorts
+ * by material first and front to back within the same material.
+ * <li>Bucket.Transparent: {@link com.jme3.renderer.queue.TransparentComparator} which
+ * sorts purely back to front by leading bounding edge with no material sort.
+ * <li>Bucket.Translucent: {@link com.jme3.renderer.queue.TransparentComparator} which
+ * sorts purely back to front by leading bounding edge with no material sort. this bucket is rendered after post processors.
+ * <li>Bucket.Sky: {@link com.jme3.renderer.queue.NullComparator} which does no sorting
+ * at all.
+ * <li>Bucket.Gui: {@link com.jme3.renderer.queue.GuiComparator} sorts geometries back to
+ * front based on their Z values.
+ */
+ public void setGeometryComparator(Bucket bucket, GeometryComparator c) {
+ switch (bucket) {
+ case Gui:
+ guiList = new GeometryList(c);
+ break;
+ case Opaque:
+ opaqueList = new GeometryList(c);
+ break;
+ case Sky:
+ skyList = new GeometryList(c);
+ break;
+ case Transparent:
+ transparentList = new GeometryList(c);
+ break;
+ case Translucent:
+ translucentList = new GeometryList(c);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown bucket type: " + bucket);
+ }
+ }
+
+ /**
+ * Adds a geometry to a shadow bucket.
+ * Note that this operation is done automatically by the
+ * {@link RenderManager}. {@link SceneProcessor}s that handle
+ * shadow rendering should fetch the queue by using
+ * {@link #getShadowQueueContent(com.jme3.renderer.queue.RenderQueue.ShadowMode) },
+ * by default no action is taken on the shadow queues.
+ *
+ * @param g The geometry to add
+ * @param shadBucket The shadow bucket type, if it is
+ * {@link ShadowMode#CastAndReceive}, it is added to both the cast
+ * and the receive buckets.
+ */
+ public void addToShadowQueue(Geometry g, ShadowMode shadBucket) {
+ switch (shadBucket) {
+ case Inherit:
+ break;
+ case Off:
+ break;
+ case Cast:
+ shadowCast.add(g);
+ break;
+ case Receive:
+ shadowRecv.add(g);
+ break;
+ case CastAndReceive:
+ shadowCast.add(g);
+ shadowRecv.add(g);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unrecognized shadow bucket type: " + shadBucket);
+ }
+ }
+
+ /**
+ * Adds a geometry to the given bucket.
+ * The {@link RenderManager} automatically handles this task
+ * when flattening the scene graph. The bucket to add
+ * the geometry is determined by {@link Geometry#getQueueBucket() }.
+ *
+ * @param g The geometry to add
+ * @param bucket The bucket to add to, usually
+ * {@link Geometry#getQueueBucket() }.
+ */
+ public void addToQueue(Geometry g, Bucket bucket) {
+ switch (bucket) {
+ case Gui:
+ guiList.add(g);
+ break;
+ case Opaque:
+ opaqueList.add(g);
+ break;
+ case Sky:
+ skyList.add(g);
+ break;
+ case Transparent:
+ transparentList.add(g);
+ break;
+ case Translucent:
+ translucentList.add(g);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown bucket type: " + bucket);
+ }
+ }
+
+ /**
+ *
+ * @param shadBucket
+ * @return
+ */
+ public GeometryList getShadowQueueContent(ShadowMode shadBucket) {
+ switch (shadBucket) {
+ case Cast:
+ return shadowCast;
+ case Receive:
+ return shadowRecv;
+ default:
+ throw new IllegalArgumentException("Only Cast or Receive are allowed");
+ }
+ }
+
+ private void renderGeometryList(GeometryList list, RenderManager rm, Camera cam, boolean clear) {
+ list.setCamera(cam); // select camera for sorting
+ list.sort();
+ for (int i = 0; i < list.size(); i++) {
+ Geometry obj = list.get(i);
+ assert obj != null;
+ rm.renderGeometry(obj);
+ obj.queueDistance = Float.NEGATIVE_INFINITY;
+ }
+ if (clear) {
+ list.clear();
+ }
+ }
+
+ public void renderShadowQueue(GeometryList list, RenderManager rm, Camera cam, boolean clear) {
+ renderGeometryList(list, rm, cam, clear);
+ }
+
+ public void renderShadowQueue(ShadowMode shadBucket, RenderManager rm, Camera cam, boolean clear) {
+ switch (shadBucket) {
+ case Cast:
+ renderGeometryList(shadowCast, rm, cam, clear);
+ break;
+ case Receive:
+ renderGeometryList(shadowRecv, rm, cam, clear);
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected shadow bucket: " + shadBucket);
+ }
+ }
+
+ public boolean isQueueEmpty(Bucket bucket) {
+ switch (bucket) {
+ case Gui:
+ return guiList.size() == 0;
+ case Opaque:
+ return opaqueList.size() == 0;
+ case Sky:
+ return skyList.size() == 0;
+ case Transparent:
+ return transparentList.size() == 0;
+ case Translucent:
+ return translucentList.size() == 0;
+ default:
+ throw new UnsupportedOperationException("Unsupported bucket type: " + bucket);
+ }
+ }
+
+ public void renderQueue(Bucket bucket, RenderManager rm, Camera cam) {
+ renderQueue(bucket, rm, cam, true);
+ }
+
+ public void renderQueue(Bucket bucket, RenderManager rm, Camera cam, boolean clear) {
+ switch (bucket) {
+ case Gui:
+ renderGeometryList(guiList, rm, cam, clear);
+ break;
+ case Opaque:
+ renderGeometryList(opaqueList, rm, cam, clear);
+ break;
+ case Sky:
+ renderGeometryList(skyList, rm, cam, clear);
+ break;
+ case Transparent:
+ renderGeometryList(transparentList, rm, cam, clear);
+ break;
+ case Translucent:
+ renderGeometryList(translucentList, rm, cam, clear);
+ break;
+
+ default:
+ throw new UnsupportedOperationException("Unsupported bucket type: " + bucket);
+ }
+ }
+
+ public void clear() {
+ opaqueList.clear();
+ guiList.clear();
+ transparentList.clear();
+ translucentList.clear();
+ skyList.clear();
+ shadowCast.clear();
+ shadowRecv.clear();
+ }
+}
diff --git a/engine/src/core/com/jme3/renderer/queue/TransparentComparator.java b/engine/src/core/com/jme3/renderer/queue/TransparentComparator.java
new file mode 100644
index 0000000..0821eba
--- /dev/null
+++ b/engine/src/core/com/jme3/renderer/queue/TransparentComparator.java
@@ -0,0 +1,102 @@
+/*
+ * 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.queue;
+
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.scene.Geometry;
+
+public class TransparentComparator implements GeometryComparator {
+
+ private Camera cam;
+ private final Vector3f tempVec = new Vector3f();
+
+ public void setCamera(Camera cam){
+ this.cam = cam;
+ }
+
+ /**
+ * Calculates the distance from a spatial to the camera. Distance is a
+ * squared distance.
+ *
+ * @param spat
+ * Spatial to distancize.
+ * @return Distance from Spatial to camera.
+ */
+ private float distanceToCam2(Geometry spat){
+ if (spat == null)
+ return Float.NEGATIVE_INFINITY;
+
+ if (spat.queueDistance != Float.NEGATIVE_INFINITY)
+ return spat.queueDistance;
+
+ Vector3f camPosition = cam.getLocation();
+ Vector3f viewVector = cam.getDirection();
+ Vector3f spatPosition = null;
+
+ if (spat.getWorldBound() != null){
+ spatPosition = spat.getWorldBound().getCenter();
+ }else{
+ spatPosition = spat.getWorldTranslation();
+ }
+
+ spatPosition.subtract(camPosition, tempVec);
+ spat.queueDistance = tempVec.dot(tempVec);
+
+ float retval = Math.abs(tempVec.dot(viewVector)
+ / viewVector.dot(viewVector));
+ viewVector.mult(retval, tempVec);
+
+ spat.queueDistance = tempVec.length();
+
+ return spat.queueDistance;
+ }
+
+ private float distanceToCam(Geometry spat){
+ // NOTE: It is best to check the distance
+ // to the bound's closest edge vs. the bound's center here.
+ return spat.getWorldBound().distanceToEdge(cam.getLocation());
+ }
+
+ public int compare(Geometry o1, Geometry o2) {
+ float d1 = distanceToCam(o1);
+ float d2 = distanceToCam(o2);
+
+ if (d1 == d2)
+ return 0;
+ else if (d1 < d2)
+ return 1;
+ else
+ return -1;
+ }
+}