Commits

Michael Ludwig committed e8709ea

More documentation improvements and package restructuring. Fixed bounding implementations, too.

  • Participants
  • Parent commits 16771d7

Comments (0)

Files changed (52)

File src/com/ferox/effect/Effect.java

 package com.ferox.effect;
 
-import com.ferox.renderer.Renderer;
+import com.ferox.renderer.RendererAware;
 
 /**
  * Designates that a class that describes or effects how a RenderAtom is
  * @author Michael Ludwig
  * 
  */
-public interface Effect {
-	/**
-	 * Get the renderer specific data that has been assigned to this Effect.
-	 * This object should not be modified unless it's by the Renderer that
-	 * created it.
-	 * 
-	 * Undefined behavior occurs if it's changed.
-	 * 
-	 * @param renderer Renderer to fetch data for, will not be null
-	 * @return The previously assigned data for the renderer, or null
-	 */
-	public Object getRenderData(Renderer renderer);
-
-	/**
-	 * Assign the renderer specific data for this object. This should not be
-	 * called directly, it is to be used by renderers to attach implementation
-	 * specific information needed for successful operation.
-	 * 
-	 * Undefined behavior occurs if this is set by something other than the
-	 * Renderer.
-	 * 
-	 * @param renderer Renderer to assign data to
-	 * @param data Object to return from getRenderData
-	 */
-	public void setRenderData(Renderer renderer, Object data);
-
+public interface Effect extends RendererAware {
 	/**
 	 * A common enum to describe the quality state effects when rendering.
 	 * DONT_CARE allows implementation to choose.

File src/com/ferox/effect/GlslShader.java

 	 * @param program GlslProgram that this shader uses when rendering
 	 * @throws NullPointerException if program is null
 	 */
-	public GlslShader(GlslProgram program) throws NullPointerException {
+	public GlslShader(GlslProgram program) {
 		if (program == null) {
 			throw new NullPointerException("Program cannot be null");
 		}
 	 * @throws IllegalArgumentException if the uniform isn't owned by this
 	 *             shader's program
 	 */
-	public void setUniform(GlslUniform uniform, Object value)
-					throws IllegalArgumentException, NullPointerException {
+	public void setUniform(GlslUniform uniform, Object value) {
 		if (uniform == null) {
 			throw new NullPointerException("Uniform cannot be null");
 		}

File src/com/ferox/effect/MultiTexture.java

 	 * @param unit Texture unit texture is assigned to
 	 * @param texture Texture object to use, null breaks old binding
 	 * 
-	 * @throws IllegalArgumentException if unit < 0
+	 * @throws IndexOutOfBoundsException if unit < 0
 	 */
-	public void setTexture(int unit, Texture texture)
-					throws IllegalArgumentException {
+	public void setTexture(int unit, Texture texture) {
 		units.setItem(unit, texture);
 	}
 
 	 * @param unit Texture unit to query bound texture
 	 * @return Texture bound to unit, null means no binding
 	 * 
-	 * @throws IllegalArgumentException if unit < 0
+	 * @throws IndexOutOfBoundsException if unit < 0
 	 */
-	public Texture getTexture(int unit) throws IllegalArgumentException {
+	public Texture getTexture(int unit) {
 		return units.getItem(unit);
 	}
 

File src/com/ferox/effect/PointStyle.java

 	 * 
 	 * @throws IllegalArgumentException if min > max
 	 */
-	public void setMinMaxPointSize(float min, float max)
-					throws IllegalArgumentException {
+	public void setMinMaxPointSize(float min, float max) {
 		if (min > max) {
 			throw new IllegalArgumentException(
 							"Cannot specify a minimum point distance that's less than the max: "

File src/com/ferox/math/AabbBoundableUtil.java

+package com.ferox.math;
+
+import org.openmali.vecmath.Vector3f;
+
+/**
+ * A utility class to compute enclosing AxisAlignedBox's for Boundables. This is
+ * to be used for Boundables when they implement their getBounds() method. All
+ * that is necessary is for them to correctly implement their getVertex() and
+ * getVertexCount() methods.
+ * 
+ * @author Michael Ludwig
+ * 
+ */
+public class AabbBoundableUtil {
+	/**
+	 * Utility method to compute the AABB of vertices and store it in box. Does
+	 * nothing if box or vertices is null, or if vertices has a vertex count of
+	 * 0.
+	 * 
+	 * @param vertices The Boundable whose aabb will be computed
+	 * @param box The AxisAlignedBox who will hold the computed results
+	 */
+	public static void getBounds(Boundable vertices, AxisAlignedBox box) {
+		if (vertices == null || box == null) {
+			return;
+		}
+		int vertexCount = vertices.getVertexCount();
+		if (vertexCount == 0) {
+			return;
+		}
+
+		Vector3f worldMax = box.getMax();
+		Vector3f worldMin = box.getMin();
+
+		worldMax.set(-Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE);
+		worldMin.set(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
+
+		for (int i = 0; i < vertexCount; i++) {
+			enclosePoint(worldMin, worldMax, vertices.getVertex(i, 0), vertices
+					.getVertex(i, 1), vertices.getVertex(i, 2));
+		}
+	}
+
+	private static void enclosePoint(Vector3f worldMin, Vector3f worldMax,
+			float x, float y, float z) {
+		worldMax.x = Math.max(worldMax.x, x);
+		worldMax.y = Math.max(worldMax.y, y);
+		worldMax.z = Math.max(worldMax.z, z);
+
+		worldMin.x = Math.min(worldMin.x, x);
+		worldMin.y = Math.min(worldMin.y, y);
+		worldMin.z = Math.min(worldMin.z, z);
+	}
+}

File src/com/ferox/math/AbstractBoundVolume.java

 		result.enclose(toEnclose);
 		return result;
 	}
-
-	@Override
-	public BoundVolume enclose(Boundable vertices, BoundVolume result) {
-		result = this.clone(result);
-		result.enclose(vertices);
-		return result;
-	}
 }

File src/com/ferox/math/AxisAlignedBox.java

 	}
 
 	@Override
-	public void enclose(BoundVolume child) throws UnsupportedOperationException {
+	public void enclose(BoundVolume child) {
 		if (child == null) {
 			return;
 		}
 	}
 
 	@Override
-	public FrustumIntersection testFrustum(View view)
-					throws NullPointerException {
+	public FrustumIntersection testFrustum(View view) {
 		if (view == null) {
 			throw new NullPointerException(
 							"Cannot test a frustum with a null view");
 	}
 
 	@Override
-	public Vector3f getExtent(Vector3f dir, boolean reverse, Vector3f out)
-					throws NullPointerException {
+	public Vector3f getExtent(Vector3f dir, boolean reverse, Vector3f out) {
 		if (dir == null) {
 			throw new NullPointerException(
 							"Can't find extent along a null direction vector");
 	 * BoundSpheres.
 	 */
 	@Override
-	public boolean intersects(BoundVolume other)
-					throws UnsupportedOperationException {
+	public boolean intersects(BoundVolume other) {
 		if (other == null) {
 			return false;
 		}
 							"Unable to compute intersection for type: " + other);
 		}
 	}
-
-	@Override
-	public void enclose(Boundable vertices) {
-		if (vertices == null) {
-			return;
-		}
-		int vertexCount = vertices.getVertexCount();
-		if (vertexCount == 0) {
-			return;
-		}
-
-		worldMax.set(-Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE);
-		worldMin.set(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
-
-		for (int i = 0; i < vertexCount; i++) {
-			enclosePoint(vertices.getVertex(i, 0), vertices.getVertex(i, 1),
-							vertices.getVertex(i, 2));
-		}
-	}
-
-	private void enclosePoint(float x, float y, float z) {
-		worldMax.x = Math.max(worldMax.x, x);
-		worldMax.y = Math.max(worldMax.y, y);
-		worldMax.z = Math.max(worldMax.z, z);
-
-		worldMin.x = Math.min(worldMin.x, x);
-		worldMin.y = Math.min(worldMin.y, y);
-		worldMin.z = Math.min(worldMin.z, z);
-	}
 	
 	@Override
 	public String toString() {

File src/com/ferox/math/BoundSphere.java

 				float or = this.radius;
 				this.radius = (dist + radius + this.radius) / 2f;
 				this.center.scaleAdd((this.radius - or) / dist, diff,
-								this.center);
+						this.center);
 			} // else we already enclose it, so do nothing
 		} else {
 			// don't need to move the center, just take the largest radius
 	 * view's sphere classifier to quickly test for frustum intersection.
 	 */
 	@Override
-	public FrustumIntersection testFrustum(View view)
-					throws NullPointerException {
+	public FrustumIntersection testFrustum(View view) {
 		if (view == null) {
 			throw new NullPointerException(
-							"Cannot test a frustum with a null view");
+					"Cannot test a frustum with a null view");
 		}
 
 		FrustumIntersection result = FrustumIntersection.INSIDE;
 
 		for (int i = View.NUM_PLANES; i >= 0; i--) {
 			if (i == lastFailedPlane
-							|| (i == View.NUM_PLANES && lastFailedPlane < 0)) {
+					|| (i == View.NUM_PLANES && lastFailedPlane < 0)) {
 				continue;
 			}
 
 	}
 
 	@Override
-	public Vector3f getExtent(Vector3f dir, boolean reverse, Vector3f result)
-					throws NullPointerException {
+	public Vector3f getExtent(Vector3f dir, boolean reverse, Vector3f result) {
 		if (dir == null) {
 			throw new NullPointerException(
-							"Can't compute extent for a null direction");
+					"Can't compute extent for a null direction");
 		}
 		if (result == null) {
 			result = new Vector3f();
 
 	/** When intersecting an AxisAlignedBox, it calls other.intersects(this). */
 	@Override
-	public boolean intersects(BoundVolume other)
-					throws UnsupportedOperationException {
+	public boolean intersects(BoundVolume other) {
 		if (other == null) {
 			return false;
 		}
 			BoundSphere s = (BoundSphere) other;
 			cross.sub(center, s.center);
 			return cross.lengthSquared() <= (radius + s.radius)
-							* (radius + s.radius);
+					* (radius + s.radius);
 		} else {
 			throw new UnsupportedOperationException(
-							"Unable to compute intersection between the given type: "
-											+ other);
+					"Unable to compute intersection between the given type: "
+							+ other);
 		}
 	}
 
 	@Override
-	public void enclose(Boundable vertices) {
-		if (vertices == null || vertices.getVertexCount() == 0) {
-			return;
-		}
-
-		float[] points = new float[vertices.getVertexCount() * 3];
-		fillPointsArray(points, vertices);
-
-		recurseMini(points, points.length / 3, 0, 0); // thanks to jME for
-		// algorithm, adapted to use float arrays
-	}
-
-	@Override
 	public String toString() {
 		return "(BoundSphere center: " + center + " radius: " + radius + ")";
 	}
 
-	private void recurseMini(float[] points, int p, int b, int ap) {
-		Vector3f tempA = BoundSphere.tempA.get();
-		Vector3f tempB = BoundSphere.tempB.get();
-		Vector3f tempC = BoundSphere.tempC.get();
-		Vector3f tempD = BoundSphere.tempD.get();
-
-		switch (b) {
-		case 0:
-			radius = 0;
-			center.set(0f, 0f, 0f);
-			break;
-		case 1:
-			radius = 1f - radiusEpsilon;
-			populateFromArray(center, points, ap - 1);
-			break;
-		case 2:
-			populateFromArray(tempA, points, ap - 1);
-			populateFromArray(tempB, points, ap - 2);
-			this.setSphere(tempA, tempB);
-			break;
-		case 3:
-			populateFromArray(tempA, points, ap - 1);
-			populateFromArray(tempB, points, ap - 2);
-			populateFromArray(tempC, points, ap - 3);
-			this.setSphere(tempA, tempB, tempC);
-			break;
-		case 4:
-			populateFromArray(tempA, points, ap - 1);
-			populateFromArray(tempB, points, ap - 2);
-			populateFromArray(tempC, points, ap - 3);
-			populateFromArray(tempD, points, ap - 4);
-			this.setSphere(tempA, tempB, tempC, tempD);
-			return;
-		}
-		for (int i = 0; i < p; i++) {
-			populateFromArray(tempA, points, i + ap);
-			float d = ((tempA.x - center.x) * (tempA.x - center.x)
-							+ (tempA.y - center.y) * (tempA.y - center.y) + (tempA.z - center.z)
-							* (tempA.z - center.z));
-			if (d - (radius * radius) > radiusEpsilon - 1f) {
-				for (int j = i; j > 0; j--) {
-					populateFromArray(tempB, points, j + ap);
-					populateFromArray(tempC, points, j - 1 + ap);
-					setInArray(tempC, points, j + ap);
-					setInArray(tempB, points, j - 1 + ap);
-				}
-				recurseMini(points, i, b + 1, ap + 1);
-			}
-		}
-	}
-
-	private static void populateFromArray(Vector3f p, float[] points, int index) {
-		p.x = points[index * 3];
-		p.y = points[index * 3 + 1];
-		p.z = points[index * 3 + 2];
-	}
-
-	private static void setInArray(Vector3f p, float[] points, int index) {
-		if (p == null) {
-			points[index * 3] = 0f;
-			points[index * 3 + 1] = 0f;
-			points[index * 3 + 2] = 0f;
-		} else {
-			points[index * 3] = p.x;
-			points[index * 3 + 1] = p.y;
-			points[index * 3 + 2] = p.z;
-		}
-	}
-
-	private void setSphere(Vector3f o, Vector3f a, Vector3f b, Vector3f c) {
-		Vector3f tA = BoundSphere.tA.get();
-		Vector3f tB = BoundSphere.tB.get();
-		Vector3f tC = BoundSphere.tC.get();
-		Vector3f tD = BoundSphere.tD.get();
-		Vector3f cross = BoundSphere.cross.get();
-
-		tA.sub(a, o);
-		tB.sub(b, o);
-		tC.sub(c, o);
-
-		float denom = 2.0f * (tA.x * (tB.y * tC.z - tC.y * tB.z) - tB.x
-						* (tA.y * tC.z - tC.y * tA.z) + tC.x
-						* (tA.y * tB.z - tB.y * tA.z));
-		if (denom == 0) {
-			center.set(0f, 0f, 0f);
-			radius = 0f;
-		} else {
-			cross.cross(tA, tB);
-			cross.scale(tC.lengthSquared());
-			tD.cross(tC, tA);
-			tD.scale(tB.lengthSquared());
-			cross.add(tD);
-			tD.cross(tB, tC);
-			tD.scale(tA.lengthSquared());
-			cross.add(tD);
-			cross.scale(1f / denom);
-
-			radius = cross.length() * radiusEpsilon;
-			center.add(o, cross);
-		}
-	}
-
-	private void setSphere(Vector3f o, Vector3f a, Vector3f b) {
-		Vector3f tA = BoundSphere.tA.get();
-		Vector3f tB = BoundSphere.tB.get();
-		Vector3f tC = BoundSphere.tC.get();
-		Vector3f tD = BoundSphere.tD.get();
-		Vector3f cross = BoundSphere.cross.get();
-
-		tA.sub(a, o);
-		tB.sub(b, o);
-		cross.cross(tA, tB);
-
-		float denom = 2f * cross.lengthSquared();
-
-		if (denom == 0) {
-			center.set(0f, 0f, 0f);
-			radius = 0f;
-		} else {
-			tC.cross(cross, tA);
-			tC.scale(tB.lengthSquared());
-			tD.cross(tB, cross);
-			tD.scale(tA.lengthSquared());
-			tC.add(tD);
-			tC.scale(1f / denom);
-
-			radius = tC.length() * radiusEpsilon;
-			center.add(o, tC);
-		}
-	}
-
-	private void setSphere(Vector3f o, Vector3f a) {
-		radius = (float) Math.sqrt(((a.x - o.x) * (a.x - o.x) + (a.y - o.y)
-						* (a.y - o.y) + (a.z - o.z) * (a.z - o.z)) / 4f)
-						+ radiusEpsilon - 1f;
-
-		center.scale(.5f, o);
-		center.scaleAdd(.5f, a, center);
-	}
-
-	private static void fillPointsArray(float[] points, Boundable verts) {
-		int vertexCount = verts.getVertexCount();
-
-		for (int i = 0; i < vertexCount; i++) {
-			points[i * 3] = verts.getVertex(i, 0);
-			points[i * 3 + 1] = verts.getVertex(i, 1);
-			points[i * 3 + 2] = verts.getVertex(i, 2);
-		}
-	}
-
-	// used in recurseMini and a few other places
+	// used in enclose
 	private static final ThreadLocal<Vector3f> tempA = new ThreadLocal<Vector3f>() {
 		@Override
 		protected Vector3f initialValue() {
 			return new Vector3f();
 		}
 	};
-	private static final ThreadLocal<Vector3f> tempD = new ThreadLocal<Vector3f>() {
-		@Override
-		protected Vector3f initialValue() {
-			return new Vector3f();
-		}
-	};
-
-	// used exclusively in setSphere methods
-	private static final ThreadLocal<Vector3f> tA = new ThreadLocal<Vector3f>() {
-		@Override
-		protected Vector3f initialValue() {
-			return new Vector3f();
-		}
-	};
-	private static final ThreadLocal<Vector3f> tB = new ThreadLocal<Vector3f>() {
-		@Override
-		protected Vector3f initialValue() {
-			return new Vector3f();
-		}
-	};
-	private static final ThreadLocal<Vector3f> tC = new ThreadLocal<Vector3f>() {
-		@Override
-		protected Vector3f initialValue() {
-			return new Vector3f();
-		}
-	};
-	private static final ThreadLocal<Vector3f> tD = new ThreadLocal<Vector3f>() {
-		@Override
-		protected Vector3f initialValue() {
-			return new Vector3f();
-		}
-	};
-
-	// used in setSphere
-	private static final ThreadLocal<Vector3f> cross = new ThreadLocal<Vector3f>() {
-		@Override
-		protected Vector3f initialValue() {
-			return new Vector3f();
-		}
-	};
 }

File src/com/ferox/math/BoundVolume.java

 	 * @throws UnsupportedOperationException if toEnclose is an unsupported
 	 *             BoundVolume implementation
 	 */
-	public void enclose(BoundVolume toEnclose)
-					throws UnsupportedOperationException;
+	public void enclose(BoundVolume toEnclose);
 
 	/**
 	 * Test for intersection between this and another BoundVolume. Return false
 	 * @throws UnsupportedOperationException if other is an unsupported
 	 *             BoundVolume implementation
 	 */
-	public boolean intersects(BoundVolume other)
-					throws UnsupportedOperationException;
+	public boolean intersects(BoundVolume other);
 
 	/**
 	 * Apply the given transform to this BoundVolume. This effectively changes
 	 * 
 	 * @throws NullPointerException if view is null
 	 */
-	public FrustumIntersection testFrustum(View view)
-					throws NullPointerException;
+	public FrustumIntersection testFrustum(View view);
 
 	/**
 	 * Compute the farthest extent of this volume along the given direction
 	 * 
 	 * @throws NullPointerException if dir is null
 	 */
-	public Vector3f getExtent(Vector3f dir, boolean reverse, Vector3f result)
-					throws NullPointerException;
+	public Vector3f getExtent(Vector3f dir, boolean reverse, Vector3f result);
 
 	/**
 	 * As enclose(toEnclose) but this BoundVolume is unmodified and the output

File src/com/ferox/math/Boundable.java

 	 *            Z_COORD
 	 * 
 	 * @return Float value for requested vertex and coordinate.
-	 * @throws IllegalArgumentException if coord or index are out of range
+	 * @throws IndexOutOfBoundsException if coord or index are out of range
 	 */
-	public float getVertex(int index, int coord)
-					throws IllegalArgumentException;
+	public float getVertex(int index, int coord);
 }

File src/com/ferox/math/BoundsCache.java

+package com.ferox.math;
+
+
+/**
+ * BoundsCache is a convenience class that implements bound volume
+ * caching for spheres and axis-aligned boxes to make it easier for Boundable
+ * implementations to implement their getBounds() method.
+ * 
+ * @author Michael Ludwig
+ * 
+ */
+public class BoundsCache {
+	private final Boundable boundable;
+
+	private final AxisAlignedBox axisCache;
+	private final BoundSphere sphereCache;
+
+	private boolean axisDirty;
+	private boolean sphereDirty;
+
+	/**
+	 * Construct a bounds cache for the given Boundable instance. 
+	 * 
+	 * @param boundable The Boundable that is used by this cache
+	 * @throws NullPointerException if boundable is null
+	 */
+	public BoundsCache(Boundable boundable) throws NullPointerException {
+		if (boundable == null) {
+			throw new NullPointerException("geom can't be null");
+		}
+
+		this.boundable = boundable;
+		
+		axisCache = new AxisAlignedBox();
+		sphereCache = new BoundSphere();
+
+		axisDirty = true;
+		sphereDirty = true;
+	}
+
+	/**
+	 * Store the bounds of this cache's geometry into result. Does nothing if
+	 * result is null or not a BoundSphere or AxisAlignedBox.
+	 * 
+	 * If the cache has been marked dirty since the last getBounds() call, the
+	 * axis or sphere cache will first be updated by calling enclose() on this
+	 * cache's geometry.
+	 * 
+	 * @param result The result that stores the bounds of this cache's Boundable
+	 */
+	public void getBounds(BoundVolume result) {
+		if (result != null) {
+			if (result instanceof AxisAlignedBox) {
+				if (axisDirty) {
+					AabbBoundableUtil.getBounds(boundable, axisCache);
+					axisDirty = false;
+				}
+				axisCache.clone(result);
+			} else if (result instanceof BoundSphere) {
+				if (sphereDirty) {
+					SphereBoundableUtil.getBounds(boundable, sphereCache);
+					sphereDirty = false;
+				}
+				sphereCache.clone(result);
+			}
+		}
+	}
+
+	/**
+	 * Mark the cache as dirty, so that the next call to getBounds() will update
+	 * the cache to reflect changes to the geometry.
+	 */
+	public void setCacheDirty() {
+		axisDirty = true;
+		sphereDirty = true;
+	}
+}

File src/com/ferox/math/Color.java

 	 * @param other Color whose values are copied
 	 * @throws NullPointerException if other is null
 	 */
-	public Color(Color other) throws NullPointerException {
+	public Color(Color other) {
 		this(other.red, other.green, other.blue, other.alpha);
 	}
 

File src/com/ferox/math/Plane.java

 	 * @throws NullPointerException if normal is null
 	 * @throws ArithmeticException if setPlane() fails
 	 */
-	public void setNormal(Vector3f normal) throws NullPointerException,
-					ArithmeticException {
+	public void setNormal(Vector3f normal) {
 		if (normal == null) {
 			throw new NullPointerException("Normal vector can't be null");
 		}
 	 * 
 	 * @throws ArithmeticException if |<a, b, c>| == 0.
 	 */
-	public void setPlane(float a, float b, float c, float d)
-					throws ArithmeticException {
+	public void setPlane(float a, float b, float c, float d) {
 		float dist = (float) Math.sqrt(a * a + b * b + c * c);
 		if (dist == 0) {
 			throw new ArithmeticException("Invalid plane input: " + a + " " + b
 	 * 
 	 * @throws NullPointerException if v is null
 	 */
-	public float signedDistance(Vector3f v) throws NullPointerException {
+	public float signedDistance(Vector3f v) {
 		float num = a * v.x + b * v.y + c * v.z + d;
 		if (len == 1f) {
 			return num;
 	 * @param trans The transform to adjust this plane with
 	 * @throws NullPointerException if trans is null
 	 */
-	public void transform(Transform trans) throws NullPointerException {
+	public void transform(Transform trans) {
 		this.transform(this, trans);
 	}
 
 	 * @return this
 	 * @throws NullPointerException if p or trans are null
 	 */
-	public Plane transform(Plane p, Transform trans)
-					throws NullPointerException {
+	public Plane transform(Plane p, Transform trans) {
 		if (trans == null || p == null) {
 			throw new NullPointerException("Can't have null input: " + trans
 							+ " " + p);
 	 * @return this
 	 * @throws NullPointerException if p or trans are null
 	 */
-	public Plane transform(Plane p, Matrix4f trans) throws NullPointerException {
+	public Plane transform(Plane p, Matrix4f trans) {
 		if (trans == null || p == null) {
 			throw new NullPointerException("Can't have null input: " + trans
 							+ " " + p);

File src/com/ferox/math/SphereBoundableUtil.java

+package com.ferox.math;
+
+import org.openmali.vecmath.Vector3f;
+
+/**
+ * A utility class to compute enclosing BoundSphere's for Boundables. This is to
+ * be used for Boundables when they implement their getBounds() method. All that
+ * is necessary is for them to correctly implement their getVertex() and
+ * getVertexCount() methods.
+ * 
+ * @author Michael Ludwig
+ * 
+ */
+public class SphereBoundableUtil {
+	private static final float radiusEpsilon = 1.00001f;
+
+	/**
+	 * Utility method to store enclose vertices in a BoundSphere, using the
+	 * recurse minimum algorithm. Does nothing if box or vertices is null, or if
+	 * vertices has a vertex count of 0.
+	 * 
+	 * @param vertices The Boundable whose bounding sphere will be computed
+	 * @param sphere The BoundSphere who will hold the computed results
+	 */
+	public static void getBounds(Boundable vertices, BoundSphere sphere) {
+		if (vertices == null || vertices.getVertexCount() == 0) {
+			return;
+		}
+
+		float[] points = new float[vertices.getVertexCount() * 3];
+		fillPointsArray(points, vertices);
+
+		// thanks to JME for the algorithm, adapted to use float[]
+		recurseMini(sphere, points, points.length / 3, 0, 0);
+	}
+
+	private static void recurseMini(BoundSphere sphere, float[] points, int p,
+			int b, int ap) {
+		Vector3f tempA = SphereBoundableUtil.tempA.get();
+		Vector3f tempB = SphereBoundableUtil.tempB.get();
+		Vector3f tempC = SphereBoundableUtil.tempC.get();
+		Vector3f tempD = SphereBoundableUtil.tempD.get();
+
+		Vector3f center = sphere.getCenter();
+
+		switch (b) {
+		case 0:
+			sphere.setRadius(0f);
+			sphere.getCenter().set(0f, 0f, 0f);
+			break;
+		case 1:
+			sphere.setRadius(1f - radiusEpsilon);
+			populateFromArray(center, points, ap - 1);
+			break;
+		case 2:
+			populateFromArray(tempA, points, ap - 1);
+			populateFromArray(tempB, points, ap - 2);
+
+			setSphere(sphere, tempA, tempB);
+			break;
+		case 3:
+			populateFromArray(tempA, points, ap - 1);
+			populateFromArray(tempB, points, ap - 2);
+			populateFromArray(tempC, points, ap - 3);
+
+			setSphere(sphere, tempA, tempB, tempC);
+			break;
+		case 4:
+			populateFromArray(tempA, points, ap - 1);
+			populateFromArray(tempB, points, ap - 2);
+			populateFromArray(tempC, points, ap - 3);
+			populateFromArray(tempD, points, ap - 4);
+
+			setSphere(sphere, tempA, tempB, tempC, tempD);
+			return;
+		}
+		for (int i = 0; i < p; i++) {
+			populateFromArray(tempA, points, i + ap);
+			float d = ((tempA.x - center.x) * (tempA.x - center.x)
+					+ (tempA.y - center.y) * (tempA.y - center.y) + (tempA.z - center.z)
+					* (tempA.z - center.z));
+			if (d - (sphere.getRadius() * sphere.getRadius()) > radiusEpsilon - 1f) {
+				for (int j = i; j > 0; j--) {
+					populateFromArray(tempB, points, j + ap);
+					populateFromArray(tempC, points, j - 1 + ap);
+					setInArray(tempC, points, j + ap);
+					setInArray(tempB, points, j - 1 + ap);
+				}
+				recurseMini(sphere, points, i, b + 1, ap + 1);
+			}
+		}
+	}
+
+	private static void populateFromArray(Vector3f p, float[] points, int index) {
+		p.x = points[index * 3];
+		p.y = points[index * 3 + 1];
+		p.z = points[index * 3 + 2];
+	}
+
+	private static void setInArray(Vector3f p, float[] points, int index) {
+		if (p == null) {
+			points[index * 3] = 0f;
+			points[index * 3 + 1] = 0f;
+			points[index * 3 + 2] = 0f;
+		} else {
+			points[index * 3] = p.x;
+			points[index * 3 + 1] = p.y;
+			points[index * 3 + 2] = p.z;
+		}
+	}
+
+	private static void setSphere(BoundSphere sphere, Vector3f o, Vector3f a,
+			Vector3f b, Vector3f c) {
+		Vector3f tA = SphereBoundableUtil.tA.get();
+		Vector3f tB = SphereBoundableUtil.tB.get();
+		Vector3f tC = SphereBoundableUtil.tC.get();
+		Vector3f tD = SphereBoundableUtil.tD.get();
+		Vector3f cross = SphereBoundableUtil.cross.get();
+
+		tA.sub(a, o);
+		tB.sub(b, o);
+		tC.sub(c, o);
+
+		float denom = 2.0f * (tA.x * (tB.y * tC.z - tC.y * tB.z) - tB.x
+				* (tA.y * tC.z - tC.y * tA.z) + tC.x
+				* (tA.y * tB.z - tB.y * tA.z));
+		if (denom == 0) {
+			sphere.getCenter().set(0f, 0f, 0f);
+			sphere.setRadius(0f);
+		} else {
+			cross.cross(tA, tB);
+			cross.scale(tC.lengthSquared());
+			tD.cross(tC, tA);
+			tD.scale(tB.lengthSquared());
+			cross.add(tD);
+			tD.cross(tB, tC);
+			tD.scale(tA.lengthSquared());
+			cross.add(tD);
+			cross.scale(1f / denom);
+
+			sphere.setRadius(cross.length() * radiusEpsilon);
+			sphere.getCenter().add(o, cross);
+		}
+	}
+
+	private static void setSphere(BoundSphere sphere, Vector3f o, Vector3f a,
+			Vector3f b) {
+		Vector3f tA = SphereBoundableUtil.tA.get();
+		Vector3f tB = SphereBoundableUtil.tB.get();
+		Vector3f tC = SphereBoundableUtil.tC.get();
+		Vector3f tD = SphereBoundableUtil.tD.get();
+		Vector3f cross = SphereBoundableUtil.cross.get();
+
+		tA.sub(a, o);
+		tB.sub(b, o);
+		cross.cross(tA, tB);
+
+		float denom = 2f * cross.lengthSquared();
+
+		if (denom == 0) {
+			sphere.getCenter().set(0f, 0f, 0f);
+			sphere.setRadius(0f);
+		} else {
+			tC.cross(cross, tA);
+			tC.scale(tB.lengthSquared());
+			tD.cross(tB, cross);
+			tD.scale(tA.lengthSquared());
+			tC.add(tD);
+			tC.scale(1f / denom);
+
+			sphere.setRadius(tC.length() * radiusEpsilon);
+			sphere.getCenter().add(o, tC);
+		}
+	}
+
+	private static void setSphere(BoundSphere sphere, Vector3f o, Vector3f a) {
+		sphere.setRadius((float) Math.sqrt(((a.x - o.x) * (a.x - o.x)
+				+ (a.y - o.y) * (a.y - o.y) + (a.z - o.z) * (a.z - o.z)) / 4f)
+				+ radiusEpsilon - 1f);
+
+		Vector3f center = sphere.getCenter();
+		center.scale(.5f, o);
+		center.scaleAdd(.5f, a, center);
+	}
+
+	private static void fillPointsArray(float[] points, Boundable verts) {
+		int vertexCount = verts.getVertexCount();
+
+		for (int i = 0; i < vertexCount; i++) {
+			points[i * 3] = verts.getVertex(i, 0);
+			points[i * 3 + 1] = verts.getVertex(i, 1);
+			points[i * 3 + 2] = verts.getVertex(i, 2);
+		}
+	}
+
+	// used in recurseMini
+	private static final ThreadLocal<Vector3f> tempA = new ThreadLocal<Vector3f>() {
+		@Override
+		protected Vector3f initialValue() {
+			return new Vector3f();
+		}
+	};
+	private static final ThreadLocal<Vector3f> tempB = new ThreadLocal<Vector3f>() {
+		@Override
+		protected Vector3f initialValue() {
+			return new Vector3f();
+		}
+	};
+	private static final ThreadLocal<Vector3f> tempC = new ThreadLocal<Vector3f>() {
+		@Override
+		protected Vector3f initialValue() {
+			return new Vector3f();
+		}
+	};
+	private static final ThreadLocal<Vector3f> tempD = new ThreadLocal<Vector3f>() {
+		@Override
+		protected Vector3f initialValue() {
+			return new Vector3f();
+		}
+	};
+
+	// used exclusively in setSphere methods
+	private static final ThreadLocal<Vector3f> tA = new ThreadLocal<Vector3f>() {
+		@Override
+		protected Vector3f initialValue() {
+			return new Vector3f();
+		}
+	};
+	private static final ThreadLocal<Vector3f> tB = new ThreadLocal<Vector3f>() {
+		@Override
+		protected Vector3f initialValue() {
+			return new Vector3f();
+		}
+	};
+	private static final ThreadLocal<Vector3f> tC = new ThreadLocal<Vector3f>() {
+		@Override
+		protected Vector3f initialValue() {
+			return new Vector3f();
+		}
+	};
+	private static final ThreadLocal<Vector3f> tD = new ThreadLocal<Vector3f>() {
+		@Override
+		protected Vector3f initialValue() {
+			return new Vector3f();
+		}
+	};
+
+	// used in setSphere
+	private static final ThreadLocal<Vector3f> cross = new ThreadLocal<Vector3f>() {
+		@Override
+		protected Vector3f initialValue() {
+			return new Vector3f();
+		}
+	};
+}

File src/com/ferox/math/Transform.java

 	 * 
 	 * @throws IllegalArgumentException if scale < .0001
 	 */
-	public Transform(Vector3f trans, float scale)
-					throws IllegalArgumentException {
+	public Transform(Vector3f trans, float scale) {
 		this();
 		setTranslation(trans);
 		setScale(scale);
 	 * 
 	 * @throws IllegalArgumentException if scale < .0001
 	 */
-	public Transform(Vector3f trans, Matrix3f rot, float scale)
-					throws IllegalArgumentException {
+	public Transform(Vector3f trans, Matrix3f rot, float scale) {
 		this();
 		setTranslation(trans);
 		setScale(scale);
 	 * 
 	 * @throws IllegalArgumentException if scale < .0001
 	 */
-	public void setScale(float scale) throws IllegalArgumentException {
+	public void setScale(float scale) {
 		if (scale < .0001f) {
 			throw new IllegalArgumentException(
 							"Can't set a scale smaller than .0001: " + scale);
 	 * 
 	 * @throws NullPointerException if t1 or t2 are null
 	 */
-	public Transform mul(Transform t1, Transform t2)
-					throws NullPointerException {
+	public Transform mul(Transform t1, Transform t2) {
 		if (t1 == null || t2 == null) {
 			throw new NullPointerException("Can't multiply null transforms");
 		}
 	 * 
 	 * @throws NullPointerException if t is null
 	 */
-	public Transform inverse(Transform t) throws NullPointerException {
+	public Transform inverse(Transform t) {
 		if (t == null) {
 			throw new NullPointerException("Can't inverse a null transform");
 		}
 	 * @return This transform
 	 * @throws NullPointerException if ti or tn are null
 	 */
-	public Transform inverseMul(Transform ti, Transform tn)
-					throws NullPointerException {
+	public Transform inverseMul(Transform ti, Transform tn) {
 		if (ti == null || tn == null) {
 			throw new NullPointerException(
 							"Can't inverse multiply null transforms");
 	 * @throws IllegalArgumentException if t is this Transform's translation
 	 *             vector
 	 */
-	public void transform(Vector3f t) throws NullPointerException,
-					IllegalArgumentException {
+	public void transform(Vector3f t) {
 		this.transform(t, t);
 	}
 
 	 * @throws IllegalArgumentException if t is this Transform's translation
 	 *             vector
 	 */
-	public Vector3f transform(Vector3f t, Vector3f result)
-					throws NullPointerException, IllegalArgumentException {
+	public Vector3f transform(Vector3f t, Vector3f result) {
 		if (t == null) {
 			throw new NullPointerException("Can't transform a null vector");
 		}
 	 * @throws IllegalArgumentException if t is this Transform's translation
 	 *             vector
 	 */
-	public void inverseTransform(Vector3f t) throws NullPointerException,
-					IllegalArgumentException {
+	public void inverseTransform(Vector3f t) {
 		this.inverseTransform(t, t);
 	}
 
 	 * @throws IllegalArgumentException if t is this Transform's translation
 	 *             vector
 	 */
-	public Vector3f inverseTransform(Vector3f t, Vector3f result)
-					throws NullPointerException, IllegalArgumentException {
+	public Vector3f inverseTransform(Vector3f t, Vector3f result) {
 		if (t == null) {
 			throw new NullPointerException("Can't transform a null vector");
 		}

File src/com/ferox/renderer/BasicRenderQueue.java

 	}
 
 	@Override
-	public int flush(Renderer renderer, View view) throws RenderException {
+	public int flush(Renderer renderer, View view) {
 		if (renderer == null || view == null) {
 			return 0;
 		}

File src/com/ferox/renderer/DepthSortingRenderQueue.java

 	 * @param forwardBack True if atoms are sorted so that closer atoms are
 	 *            rendered first
 	 */
-	public DepthSortingRenderQueue(boolean forwardBack)
-					throws NullPointerException {
+	public DepthSortingRenderQueue(boolean forwardBack) {
 		sorter = new DepthSorter(forwardBack);
 	}
 

File src/com/ferox/renderer/FrameStatistics.java

 	 * @param out The PrintStream that will display the stats summary
 	 * @throws NullPointerException if out is null
 	 */
-	public void reportStatistics(PrintStream out) throws NullPointerException {
+	public void reportStatistics(PrintStream out) {
 		out.printf("Total Time: %.6f ms (idle: %.6f ms,\n",
 						getTotalTime() / 1e6f, getIdleTime() / 1e6f);
 		out.printf("                     in prepare: %.6f ms,\n",

File src/com/ferox/renderer/RenderAtom.java

 	 * @throws NullPointerException if t is null
 	 * @throws IllegalArgumentException if key is incorrect
 	 */
-	public void setTransform(Transform t, Object key)
-					throws IllegalArgumentException, NullPointerException {
+	public void setTransform(Transform t, Object key) {
 		if (this.key == null || key == this.key) {
 			if (t == null) {
 				throw new NullPointerException("Transform cannot be null");
 	 * 
 	 * @throws IllegalArgumentException if key is incorrect
 	 */
-	public void setEffects(EffectSet a, Object key)
-					throws IllegalArgumentException {
+	public void setEffects(EffectSet a, Object key) {
 		if (this.key == null || key == this.key) {
 			effects = a;
 		} else {
 	 * @throws NullPointerException if g is null
 	 * @throws IllegalArgumentException if key is incorrect
 	 */
-	public void setGeometry(Geometry g, Object key)
-					throws IllegalArgumentException, NullPointerException {
+	public void setGeometry(Geometry g, Object key) {
 		if (this.key == null || key == this.key) {
 			if (g == null) {
 				throw new NullPointerException("Geometry cannot be null");

File src/com/ferox/renderer/RenderQueue.java

 	 * @param view The current View that represents the viewing location for all
 	 *            rendered atoms
 	 */
-	public int flush(Renderer renderer, View view) throws RenderException;
+	public int flush(Renderer renderer, View view);
 
 	/**
 	 * Add the given atom to be rendered by this RenderQueue. If an atom is

File src/com/ferox/renderer/Renderer.java

 	 */
 	public WindowSurface createWindowSurface(DisplayOptions options, int x,
 					int y, int width, int height, boolean resizable,
-					boolean undecorated) throws SurfaceCreationException,
-					RenderStateException;
+					boolean undecorated);
 
 	/**
 	 * Create a surface that puts the application into exclusive fullscreen
 	 *             renderer is destroyed
 	 */
 	public FullscreenSurface createFullscreenSurface(DisplayOptions options,
-					int width, int height) throws SurfaceCreationException,
-					RenderStateException;
+					int width, int height);
 
 	/**
 	 * Create a texture surface that can be used to render into textures. The
 	 */
 	public TextureSurface createTextureSurface(DisplayOptions options,
 					TextureTarget target, int width, int height, int depth,
-					int layer, int numColorTargets, boolean useDepthRenderBuffer)
-					throws SurfaceCreationException, RenderStateException;
+					int layer, int numColorTargets, boolean useDepthRenderBuffer);
 
 	/**
 	 * Create a texture surface that uses the exact same textures as the given
 	 * @throws RenderStateException if the renderer isn't idle or if it's been
 	 *             destroyed
 	 */
-	public TextureSurface createTextureSurface(TextureSurface share, int layer)
-					throws SurfaceCreationException, RenderStateException;
+	public TextureSurface createTextureSurface(TextureSurface share, int layer);
 
 	/**
 	 * Destroy the given RenderSurface. After a call to this method, the surface
 	 * @throws RenderStateException if this renderer isn't idle or if it's been
 	 *             destroyed
 	 */
-	public void destroy(RenderSurface surface) throws NullPointerException,
-					IllegalArgumentException, RenderStateException;
+	public void destroy(RenderSurface surface);
 
 	/**
 	 * Destroy all remaining, undestroyed RenderSurfaces created by this
 	 * @throws RenderStateException if the Renderer isn't idle, or if it's
 	 *             already been destroyed
 	 */
-	public void destroy() throws RenderStateException;
+	public void destroy();
 
 	/**
 	 * Add the given ResourceManager to the Renderer's list of ResourceManagers.
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public void addResourceManager(ResourceManager manager)
-					throws RenderStateException;
+	public void addResourceManager(ResourceManager manager);
 
 	/**
 	 * Remove the given ResourceManager from the Renderer's list of managers.
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public void removeResourceManager(ResourceManager manager)
-					throws RenderStateException;
+	public void removeResourceManager(ResourceManager manager);
 
 	/**
 	 * Request the given resource to be updated. If forceFullUpdate is true,
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public void requestUpdate(Resource resource, boolean forceFullUpdate)
-					throws RenderStateException, NullPointerException;
+	public void requestUpdate(Resource resource, boolean forceFullUpdate);
 
 	/**
 	 * Request the given resource to be cleaned up. This is the counterpart to
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public void requestCleanUp(Resource resource) throws RenderStateException,
-					NullPointerException;
+	public void requestCleanUp(Resource resource);
 
 	/**
 	 * Queue the given RenderSurface to be rendered during the next call to
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public Renderer queueRender(RenderSurface surface)
-					throws RenderStateException, IllegalArgumentException;
+	public Renderer queueRender(RenderSurface surface);
 
 	/**
 	 * Render a single frame. The renderer must invoke manage() on its default
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public FrameStatistics flushRenderer(FrameStatistics store)
-					throws RenderException, RenderStateException;
+	public FrameStatistics flushRenderer(FrameStatistics store);
 
 	/* Anytime operations. */
 
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public Status getStatus(Resource resource) throws RenderStateException;
+	public Status getStatus(Resource resource);
 
 	/**
 	 * Get a Renderer status message that is more informative about the given
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public String getStatusMessage(Resource resource)
-					throws RenderStateException;
+	public String getStatusMessage(Resource resource);
 
 	/**
 	 * Get the capabilities of this Renderer.
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public RenderCapabilities getCapabilities() throws RenderStateException;
+	public RenderCapabilities getCapabilities();
 
 	/* Resource operations. */
 
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public Status update(Resource resource, boolean forceFullUpdate)
-					throws RenderStateException, UnsupportedResourceException,
-					NullPointerException, RenderException;
+	public Status update(Resource resource, boolean forceFullUpdate);
 
 	/**
 	 * Cleanup the low-level, graphics hardware related data for the given
 	 * @throws RenderStateException if the Renderer isn't idle or if it's
 	 *             already been destroyed
 	 */
-	public void cleanUp(Resource resource) throws RenderStateException,
-					UnsupportedResourceException, NullPointerException,
-					RenderException, IllegalArgumentException;
+	public void cleanUp(Resource resource);
 
 	/* Rendering operations. */
 
 	 * @throws RenderStateException if the Renderer isn't prepared to render
 	 *             atoms from this thread
 	 */
-	public int renderAtom(RenderAtom atom) throws RenderStateException,
-					NullPointerException, UnsupportedResourceException,
-					UnsupportedEffectException;
+	public int renderAtom(RenderAtom atom);
 }

File src/com/ferox/renderer/RendererAware.java

+package com.ferox.renderer;
+
+/** Generic interface that lets Renderers attach internal data
+ * objects to this for future fast access, without needing to
+ * a potentially expensive Map look-up.
+ * 
+ * It is recommended that implementations use the RenderDataCache.
+ * 
+ * @author Michael Ludwig
+ *
+ */
+public interface RendererAware {
+	/**
+	 * Get the renderer specific data that has been assigned to this Effect.
+	 * This object should not be modified unless it's by the Renderer that
+	 * created it.
+	 * 
+	 * Undefined behavior occurs if it's changed.
+	 * 
+	 * @param renderer Renderer to fetch data for, will not be null
+	 * @return The previously assigned data for the renderer, or null
+	 */
+	public Object getRenderData(Renderer renderer);
+
+	/**
+	 * Assign the renderer specific data for this object. This should not be
+	 * called directly, it is to be used by renderers to attach implementation
+	 * specific information needed for successful operation.
+	 * 
+	 * Undefined behavior occurs if this is set by something other than the
+	 * Renderer.
+	 * 
+	 * @param renderer Renderer to assign data to
+	 * @param data Object to return from getRenderData
+	 */
+	public void setRenderData(Renderer renderer, Object data);
+}

File src/com/ferox/renderer/StateSortingRenderQueue.java

 	 * @param sortPriority Sort priority to use when sorting by effect
 	 * @throws NullPointerException if any element in sortPriority is null
 	 */
-	public StateSortingRenderQueue(Role[] sortPriority)
-					throws NullPointerException {
+	public StateSortingRenderQueue(Role[] sortPriority) {
 		if (sortPriority == null) { // use a default sort order
 			sortPriority = new Role[] { Role.SHADER, Role.TEXTURE,
 							Role.MATERIAL, Role.BLEND_MODE,

File src/com/ferox/renderer/View.java

 	 * @param plane The requested plane
 	 * @return The Plane instance for the requested plane, in world coordinates
 	 * 
-	 * @throws ArrayIndexOutOfBoundsException if plane isn't in [0, 5]
+	 * @throws IndexOutOfBoundsException if plane isn't in [0, 5]
 	 */
-	public Plane getWorldPlane(int plane) throws ArrayIndexOutOfBoundsException {
+	public Plane getWorldPlane(int plane) {
 		return worldPlanes[plane];
 	}
 
 	 *             far, or if near <= 0
 	 * 
 	 */
-	public void setPerspective(float fov, float aspect, float near, float far)
-					throws IllegalArgumentException, IllegalStateException {
+	public void setPerspective(float fov, float aspect, float near, float far) {
 		float h = (float) Math.tan(Math.toRadians(fov)) * near * .5f;
 		float w = h * aspect;
 		this.useOrtho = false;
 	 *             far, or near <= 0 when the view isn't orthographic
 	 */
 	public void setFrustum(float left, float right, float bottom, float top,
-					float near, float far) throws IllegalArgumentException {
+					float near, float far) {
 		if (left > right || bottom > top || near > far) {
 			throw new IllegalArgumentException(
 							"Frustum values would create an invalid frustum: "
 	 * @throws IllegalStateException if ortho is false and the near frustum
 	 *             plane is <= 0
 	 */
-	public void setOrthogonalProjection(boolean ortho)
-					throws IllegalStateException {
+	public void setOrthogonalProjection(boolean ortho) {
 		if (!ortho && frustumNear <= 0) {
 			throw new IllegalStateException(
 							"Calling setOrthogonalProjection(false) when near frustum distance <= 0 is illegal");
 	 * @throws IllegalArgumentException if any value is outside of [0, 1], or if
 	 *             top < bottom, or if left > right.
 	 */
-	public void setViewPort(float left, float right, float bottom, float top)
-					throws IllegalArgumentException {
+	public void setViewPort(float left, float right, float bottom, float top) {
 		if (left < 0 || left > 1) {
 			throw new IllegalArgumentException(
 							"Illegal value for left viewport edge: " + left);

File src/com/ferox/resource/BufferData.java

 package com.ferox.resource;
 
-
-/** BufferData represents an abstract block of data.  Internally
- * it relies on primitive arrays of floats, ints, shorts, or bytes
- * to store the actual data.  A null array is allowed, but it must have
- * a special interpretation by the Renderer that needs to use BufferData's.
+/**
+ * BufferData represents an abstract block of data. Internally it relies on
+ * primitive arrays of floats, ints, shorts, or bytes to store the actual data.
+ * A null array is allowed, but it must have a special interpretation by the
+ * Renderer that needs to use BufferData's.
  * 
- * BufferData itself is not a Resource, but instead many resources will
- * rely on BufferData to perform type and size checking for it.  To make 
- * things simpler, a BufferData's type and capacity cannot be changed after
- * it is constructed.  Only the contents of the buffer data may be modified
- * or reassigned.
+ * BufferData itself is not a Resource, but instead many resources will rely on
+ * BufferData to perform type and size checking for it. To make things simpler,
+ * a BufferData's type and capacity cannot be changed after it is constructed.
+ * Only the contents of the buffer data may be modified or reassigned.
  * 
  * If a BufferData is constructed with a null array, and an associated resource
  * is updated before a non-null array is assigned, then the Renderer should
- * allocate space without changing any of the values (which could be old garbage).
+ * allocate space without changing any of the values (which could be old
+ * garbage).
  * 
  * If a BufferData is later set to have a null array, and an associated resource
- * is updated, Renderers should not modify already allocated space.  Essentially,
- * a null data array implies Renderers must make sure the space is there, but otherwise
- * not change the contents.
+ * is updated, Renderers should not modify already allocated space. Essentially,
+ * a null data array implies Renderers must make sure the space is there, but
+ * otherwise not change the contents.
  * 
  * Because of this, care should be given when using null arrays and BufferDatas.
- * One reasonably safe scenario is to update a resource using a BufferData, and then
- * on a success, nullify the BufferData's array to clear up client memory.  It is
- * necessary to reload the array before any more updates.
+ * One reasonably safe scenario is to update a resource using a BufferData, and
+ * then on a success, nullify the BufferData's array to clear up client memory.
+ * It is necessary to reload the array before any more updates.
  * 
  * @author Michael Ludwig
- *
+ * 
  */
 public class BufferData {
 	public static final int BYTESIZE_BYTE = 1;
 	public static final int BYTESIZE_SHORT = 2;
 	public static final int BYTESIZE_INT = 4;
 	public static final int BYTESIZE_FLOAT = 4;
-	
-	/** DataType represents one of the data types common to game-related 
-	 * graphics applications.  It does not allow for double, long or char types
-	 * because they are not likely to be used in games to represent large blocks
-	 * of data.
+
+	/**
+	 * DataType represents one of the data types common to game-related graphics
+	 * applications. It does not allow for double, long or char types because
+	 * they are not likely to be used in games to represent large blocks of
+	 * data.
 	 * 
-	 * DataType allows for the distinction between signed and unsigned types.  If
-	 * a type is unsigned, the primitives are not treated as two's complement integers. */
+	 * DataType allows for the distinction between signed and unsigned types. If
+	 * a type is unsigned, the primitives are not treated as two's complement
+	 * integers.
+	 */
 	public static enum DataType {
-		FLOAT(float[].class, BYTESIZE_FLOAT, false), 
-		INT(int[].class, BYTESIZE_INT, false), 
-		SHORT(short[].class, BYTESIZE_SHORT, false), 
+		/** Use float[] for the data. */
+		FLOAT(float[].class, BYTESIZE_FLOAT, false),
+		/** Use signed int[] for the data. */
+		INT(int[].class, BYTESIZE_INT, false),
+		/** Use signed short[] for the data. */
+		SHORT(short[].class, BYTESIZE_SHORT, false),
+		/** Use signed byte[] for the data. */
 		BYTE(byte[].class, BYTESIZE_BYTE, false),
-		
-		UNSIGNED_INT(int[].class, BYTESIZE_INT, true), 
+		/** Used int[] for the data, bit pattern treated as unsigned. */
+		UNSIGNED_INT(int[].class, BYTESIZE_INT, true),
+		/** Use short[] for the data, bit pattern treated as unsigned. */
 		UNSIGNED_SHORT(short[].class, BYTESIZE_SHORT, true),
+		/** Use byte[] for the data, bit pattern treated as unsigned. */
 		UNSIGNED_BYTE(byte[].class, BYTESIZE_BYTE, true);
-		
-		private Class<?> classType; private int byteSize; private boolean unsigned;
+
+		private Class<?> classType;
+		private int byteSize;
+		private boolean unsigned;
+
 		private DataType(Class<?> type, int byteSize, boolean unsigned) {
-			this.classType = type; this.byteSize = byteSize; this.unsigned = unsigned;
+			classType = type;
+			this.byteSize = byteSize;
+			this.unsigned = unsigned;
 		}
-		
-		/** Return true if the object is a primitive array of the 
-		 * corresponding type to this DataType. */
+
+		/**
+		 * Return true if the object is a primitive array of the corresponding
+		 * type to this DataType.
+		 * 
+		 * @param instance Object to test validity on
+		 * @return Whether or not instance matches this DataType
+		 */
 		public boolean isValid(Object instance) {
-			return (instance == null || this.classType.isInstance(instance));
+			return (instance == null || classType.isInstance(instance));
 		}
-		
-		/** Get the number of bytes in each of this type's primitives. */
+
+		/**
+		 * Get the number of bytes in each of this type's primitives.
+		 * 
+		 * @return The number of bytes in each primitive element
+		 */
 		public int getByteSize() {
-			return this.byteSize;
+			return byteSize;
 		}
-		
-		/** Return true if the primitives should be treated as unsigned. */
+
+		/**
+		 * Return true if the primitives should be treated as unsigned.
+		 * 
+		 * @return Whether or not primitives are signed or unsigned
+		 */
 		public boolean isUnsigned() {
-			return this.unsigned;
+			return unsigned;
 		}
-		
-		/** Determine the DataType based on the given object and whether
-		 * or not is unsigned.  Returns null if array is null or if the
-		 * object is not a valid primitive array.
+
+		/**
+		 * Determine the DataType based on the given object and whether or not
+		 * is unsigned. Returns null if array is null or if the object is not a
+		 * valid primitive array.
 		 * 
-		 * unsigned is ignored for float[] arrays.  For int[], short[], and
-		 * byte[] arrays, unsigned distinguishes between returning 
-		 * X or UNSIGNED_X. */
+		 * unsigned is ignored for float[] arrays. For int[], short[], and
+		 * byte[] arrays, unsigned distinguishes between returning X or
+		 * UNSIGNED_X.
+		 * 
+		 * @param array Object to detect its DataType
+		 * @param unsigned Whether or not to treat array as unsigned, if
+		 *            applicable
+		 * @return The matching data type, or null if array doesn't match
+		 */
 		public static DataType getDataType(Object array, boolean unsigned) {
-			if (array == null)
+			if (array == null) {
 				return null;
-			
+			}
+
 			if (array instanceof float[]) {
 				return DataType.FLOAT;
 			} else if (unsigned) {
-				if (array instanceof int[])
+				if (array instanceof int[]) {
 					return DataType.UNSIGNED_INT;
-				else if (array instanceof short[])
+				} else if (array instanceof short[]) {
 					return DataType.UNSIGNED_SHORT;
-				else if (array instanceof byte[])
+				} else if (array instanceof byte[]) {
 					return DataType.UNSIGNED_BYTE;
+				}
 			} else {
-				if (array instanceof int[])
+				if (array instanceof int[]) {
 					return DataType.INT;
-				else if (array instanceof short[])
+				} else if (array instanceof short[]) {
 					return DataType.SHORT;
-				else if (array instanceof byte[])
+				} else if (array instanceof byte[]) {
 					return DataType.BYTE;
+				}
 			}
-			
+
 			return null;
 		}
 	}
-	
-	private int capacity;
-	private DataType type;
+
+	private final int capacity;
+	private final DataType type;
 	private Object data;
-	
-	/** Create a BufferData object wrapping the given primitive array.
-	 * data must not be null and be a valid primitive array (byte[], short[],
-	 * int[], or float[]).
+
+	/**
+	 * Create a BufferData object wrapping the given primitive array. data must
+	 * not be null and be a valid primitive array (byte[], short[], int[], or
+	 * float[]).
 	 * 
-	 * This constructor identifies the capacity as the length of the array,
-	 * and determines the DataType as per getDataType(data, unsigned).
+	 * This constructor identifies the capacity as the length of the array, and
+	 * determines the DataType as per getDataType(data, unsigned).
 	 * 
-	 * Throw an exception if data is null, or if data isn't a valid array. */
-	public BufferData(Object data, boolean unsigned) throws NullPointerException, IllegalArgumentException {
-		if (data == null)
-			throw new NullPointerException("Constructor expects a non-null primitive array");
-		
-		this.type = DataType.getDataType(data, unsigned);
-		if (this.type == null)
-			throw new IllegalArgumentException("Data must be a valid data type, according to DataType: " + data);
-		
+	 * @param data The primitive array used by this BufferData
+	 * @param unsigned Whether or not this data is to be unsigned
+	 * 
+	 * @throws NullPointerException if data is null
+	 * @throws IllegalArgumentException if data doesn't match a DataType
+	 */
+	public BufferData(Object data, boolean unsigned) {
+		if (data == null) {
+			throw new NullPointerException(
+					"Constructor expects a non-null primitive array");
+		}
+
+		type = DataType.getDataType(data, unsigned);
+		if (type == null) {
+			throw new IllegalArgumentException(
+					"Data must be a valid data type, according to DataType: "
+							+ data);
+		}
+
 		this.data = data;
-		this.capacity = capacity(data);
+		capacity = capacity(data);
 	}
-	
-	/** Create a BufferData object with a null data array.  Initialize
-	 * it with the given capacity and type.
+
+	/**
+	 * Create a BufferData object with a null data array. Initialize it with the
+	 * given capacity and type.
 	 * 
-	 * Throw an exception if capacity < 0, or if type is null. */
-	public BufferData(int capacity, DataType type) throws NullPointerException, IllegalArgumentException {
-		if (type == null)
+	 * @param capacity Number of primitives that this BufferData will hold
+	 * @param type The DataType to use for this BufferData
+	 * 
+	 * @throws NullPointerException if type is null
+	 * @throws IllegalArgumentException if capacity < 0
+	 */
+	public BufferData(int capacity, DataType type) {
+		if (type == null) {
 			throw new NullPointerException("Must specify a non-null DataType");
-		if (capacity < 0)
-			throw new IllegalArgumentException("Must specifiy a capacity >= 0, not: " + capacity);
-		
+		}
+		if (capacity < 0) {
+			throw new IllegalArgumentException(
+					"Must specifiy a capacity >= 0, not: " + capacity);
+		}
+
 		this.capacity = capacity;
 		this.type = type;
-		this.data = null;
+		data = null;
 	}
-	
-	/** Return the data type that represents the array type
-	 * for this BufferData.  If the returned type is
-	 * INT or UNSIGNED_INT, getData() is an int[].
-	 * If it's SHORT or UNSIGNED_SHORT, it's a short[].
-	 * If it's BYTE or UNSIGNED_BYTE, it's a byte[].
-	 * If it's FLAOT, then the data is a float[]. */
+
+	/**
+	 * Return the data type that represents the array type for this BufferData.
+	 * If the returned type is INT or UNSIGNED_INT, getData() is an int[]. If
+	 * it's SHORT or UNSIGNED_SHORT, it's a short[]. If it's BYTE or
+	 * UNSIGNED_BYTE, it's a byte[]. If it's FLAOT, then the data is a float[].
+	 * 
+	 * @return The DataType that matches this BufferData's data array
+	 */
 	public DataType getType() {
-		return this.type;
+		return type;
 	}
-	
-	/** Return the capacity of this buffer data.  If getData()
-	 * is not null, it will have a length of getCapacity().  If it
-	 * is null, the Renderer must allocate space of the given capacity
-	 * to be filled later. */
+
+	/**
+	 * Return the capacity of this buffer data. If getData() is not null, it
+	 * will have a length of getCapacity(). If it is null, the Renderer must
+	 * allocate space of the given capacity to be filled later.
+	 * 
+	 * @return The length of the data array, if it's not null
+	 */
 	public int getCapacity() {
-		return this.capacity;
+		return capacity;
 	}
-	
-	/** Return the primitive array that is holding the data of this
-	 * BufferData.  This return value may be null.  If it is not null,
-	 * it is guaranteed to be a short[], int[], float[] or byte[]. */
+
+	/**
+	 * Return the primitive array that is holding the data of this BufferData.
+	 * This return value may be null. If it is not null, it is guaranteed to be
+	 * a short[], int[], float[] or byte[].
+	 * 
+	 * @return The data array currently stored, if not null, it's length is
+	 *         getCapacity()
+	 */
 	public Object getData() {
-		return this.data;
+		return data;
 	}
-	
-	/** Set the primitive array object to be used by this BufferData.
-	 * If obj is not null, the object must be a primitive array matching
-	 * this BufferData's type.  It must also have a length that equals the
-	 * capacity of the buffer data.  If obj is null, the original array reference
-	 * is cleared but the type and capacity remain unchanged.
+
+	/**
+	 * Set the primitive array object to be used by this BufferData. If obj is
+	 * not null, the object must be a primitive array matching this BufferData's
+	 * type. It must also have a length that equals the capacity of the buffer
+	 * data. If obj is null, the original array reference is cleared but the
+	 * type and capacity remain unchanged.
 	 * 
-	 * Throws an exception if obj isn't null and doesn't have a matching DataType
-	 * or if its array length doesn't match the capacity. */
+	 * @param obj The new data array to use for this BufferData, may be null
+	 * 
+	 * @throws IllegalArgumentException if obj doesn't have a matching type or
+	 *             size (only matters when obj != null)
+	 */
 	public void setData(Object obj) throws IllegalArgumentException {
 		if (obj != null) {
-			DataType t = DataType.getDataType(obj, this.type.isUnsigned());
-			if (t != this.type)
-				throw new IllegalArgumentException("Data object does not match BufferData's type.  Expected: " + this.type + " but was: " + t);
+			DataType t = DataType.getDataType(obj, type.isUnsigned());
+			if (t != type) {
+				throw new IllegalArgumentException(
+						"Data object does not match BufferData's type.  Expected: "
+								+ type + " but was: " + t);
+			}
 			int size = capacity(obj);
-			if (size != this.capacity)
-				throw new IllegalArgumentException("Data object must have a length matching this BufferData's capacity. Expected: " + this.capacity + " but was: " + size);
+			if (size != capacity) {
+				throw new IllegalArgumentException(
+						"Data object must have a length matching this BufferData's capacity. Expected: "
+								+ capacity + " but was: " + size);
+			}
 		}
 		// obj is valid, so we can assign it now
-		this.data = obj;
+		data = obj;
 	}
-	
+
 	private static int capacity(Object array) {
-		if (array instanceof float[])
+		if (array instanceof float[]) {
 			return ((float[]) array).length;
-		else if (array instanceof int[])
+		} else if (array instanceof int[]) {
 			return ((int[]) array).length;
-		else if (array instanceof short[])
+		} else if (array instanceof short[]) {
 			return ((short[]) array).length;
-		else if (array instanceof byte[])
+		} else if (array instanceof byte[]) {
 			return ((byte[]) array).length;
-		
+		}
+
 		return 0;
 	}
 }

File src/com/ferox/resource/Geometry.java

 
 import com.ferox.math.Boundable;
 
-/** A Geometry represents an abstract representation of something
- * on the screen. A Geometry is the union of a boundable and
- * a resource.  By being a boundable, it allows bounding volumes to 
- * be created around it.  By extending a resource, creating and 
- * modifying the geometry must follow the same process as any other
+/**
+ * A Geometry represents an abstract representation of something on the screen.
+ * A Geometry is the union of a boundable and a resource. By being a boundable,
+ * it allows bounding volumes to be created around it. By extending a resource,
+ * creating and modifying the geometry must follow the same process as any other
  * resource, allowing optimizations to be performed.
  * 
- * Attempting to render a Geometry that has a status of ERROR
- * depends on the implementation of Renderer.  It should not cause
- * rendering to fail, and likely nothing will be rendered for the
- * erroneous geometry.
+ * There are two implementations of Geometry available that a Renderer must
+ * support: PolygonGeometry and IndexArrayGeometry. Instead of implementing more
+ * types of Geometry, programmers should sub-class one of those two and
+ * automatically configure them.
  * 
- * Geometry provides no additional methods to Boundable 
<