Michael Ludwig avatar Michael Ludwig committed 30cc7c3

Rename geometry+texture utilities package to live under the resource package.

Comments (0)

Files changed (50)

ferox-demos/src/main/java/com/ferox/physics/GravityTest.java

 import com.ferox.physics.dynamics.RigidBody;
 import com.ferox.renderer.OnscreenSurface;
 import com.ferox.resource.VertexBufferObject.StorageMode;
+import com.ferox.resource.geom.Geometry;
 import com.ferox.scene.AmbientLight;
 import com.ferox.scene.BlinnPhongMaterial;
 import com.ferox.scene.Camera;
 import com.ferox.scene.PointLight;
 import com.ferox.scene.Renderable;
 import com.ferox.scene.Transform;
-import com.ferox.util.geom.Geometry;
 import com.lhkbob.entreri.Entity;
 
 public class GravityTest extends PhysicsApplicationStub {
                                            .75 * BOUNDS, 0, 0, 0, 1));
 
         // shapes
-        Geometry geomShape1 = com.ferox.util.geom.Box.create(2 + 2 * MARGIN, COMPILE_TYPE);
+        Geometry geomShape1 = com.ferox.resource.geom.Box.create(2 + 2 * MARGIN, COMPILE_TYPE);
         com.ferox.physics.collision.Shape physShape1 = new com.ferox.physics.collision.shape.Box(2,
                                                                                                  2,
                                                                                                  2);
 
-        Geometry geomShape2 = com.ferox.util.geom.Box.create(2 + 2 * MARGIN, COMPILE_TYPE);
+        Geometry geomShape2 = com.ferox.resource.geom.Box.create(2 + 2 * MARGIN, COMPILE_TYPE);
         com.ferox.physics.collision.Shape physShape2 = new com.ferox.physics.collision.shape.Box(2,
                                                                                                  2,
                                                                                                  2);

ferox-demos/src/main/java/com/ferox/physics/PhysicsTest.java

 import com.ferox.physics.dynamics.RigidBody;
 import com.ferox.renderer.OnscreenSurface;
 import com.ferox.resource.VertexBufferObject.StorageMode;
+import com.ferox.resource.geom.Box;
+import com.ferox.resource.geom.Geometry;
 import com.ferox.scene.AmbientLight;
 import com.ferox.scene.AtmosphericFog;
 import com.ferox.scene.AtmosphericFog.Falloff;
 import com.ferox.scene.PointLight;
 import com.ferox.scene.Renderable;
 import com.ferox.scene.Transform;
-import com.ferox.util.geom.Box;
-import com.ferox.util.geom.Geometry;
 import com.lhkbob.entreri.Entity;
 
 public class PhysicsTest extends PhysicsApplicationStub {

ferox-demos/src/main/java/com/ferox/renderer/impl/jogl/FixedFunctionRenderTest.java

 import com.ferox.resource.Texture.Target;
 import com.ferox.resource.TextureFormat;
 import com.ferox.resource.VertexBufferObject.StorageMode;
+import com.ferox.resource.geom.Geometry;
+import com.ferox.resource.geom.Sphere;
 import com.ferox.util.ApplicationStub;
-import com.ferox.util.geom.Geometry;
-import com.ferox.util.geom.Sphere;
 
 public class FixedFunctionRenderTest extends ApplicationStub {
     private FixedFunctionPass pass;

ferox-demos/src/main/java/com/ferox/renderer/impl/jogl/GlslRenderTest.java

 import com.ferox.resource.Texture;
 import com.ferox.resource.Texture.Filter;
 import com.ferox.resource.Texture.Target;
+import com.ferox.resource.geom.Box;
+import com.ferox.resource.geom.Geometry;
 import com.ferox.resource.TextureFormat;
 import com.ferox.util.ApplicationStub;
-import com.ferox.util.geom.Box;
-import com.ferox.util.geom.Geometry;
 
 public class GlslRenderTest extends ApplicationStub {
     private static final String VERTEX_SHADER = "uniform mat4 projection;" + "uniform mat4 modelview;" + "uniform vec2 transform;" +

ferox-demos/src/main/java/com/ferox/renderer/impl/lwjgl/FixedFunctionRenderTest.java

 import com.ferox.resource.Texture.Target;
 import com.ferox.resource.TextureFormat;
 import com.ferox.resource.VertexBufferObject.StorageMode;
+import com.ferox.resource.geom.Geometry;
+import com.ferox.resource.geom.Sphere;
 import com.ferox.util.ApplicationStub;
-import com.ferox.util.geom.Geometry;
-import com.ferox.util.geom.Sphere;
 
 public class FixedFunctionRenderTest extends ApplicationStub {
     private FixedFunctionPass pass;

ferox-demos/src/main/java/com/ferox/renderer/impl/lwjgl/GlslRenderTest.java

 import com.ferox.resource.Texture;
 import com.ferox.resource.Texture.Filter;
 import com.ferox.resource.Texture.Target;
+import com.ferox.resource.geom.Box;
+import com.ferox.resource.geom.Geometry;
 import com.ferox.resource.TextureFormat;
 import com.ferox.util.ApplicationStub;
-import com.ferox.util.geom.Box;
-import com.ferox.util.geom.Geometry;
 
 public class GlslRenderTest extends ApplicationStub {
     private static final String VERTEX_SHADER = "uniform mat4 projection;" + "uniform mat4 modelview;" + "uniform vec2 transform;" +

ferox-demos/src/main/java/com/ferox/renderer/impl/lwjgl/LightBlendingTest.java

 import com.ferox.renderer.Renderer.BlendFunction;
 import com.ferox.renderer.Renderer.Comparison;
 import com.ferox.renderer.Task;
+import com.ferox.resource.geom.Box;
+import com.ferox.resource.geom.Geometry;
 import com.ferox.util.ApplicationStub;
-import com.ferox.util.geom.Box;
-import com.ferox.util.geom.Geometry;
 
 public class LightBlendingTest extends ApplicationStub {
     private Geometry shape;

ferox-demos/src/main/java/com/ferox/scene/controller/ffp/SimpleTest.java

 import com.ferox.renderer.impl.jogl.JoglFramework;
 import com.ferox.renderer.impl.lwjgl.LwjglFramework;
 import com.ferox.resource.VertexBufferObject.StorageMode;
+import com.ferox.resource.geom.Box;
+import com.ferox.resource.geom.Geometry;
+import com.ferox.resource.geom.Sphere;
+import com.ferox.resource.geom.Teapot;
 import com.ferox.scene.AmbientLight;
 import com.ferox.scene.BlinnPhongMaterial;
 import com.ferox.scene.Camera;
 import com.ferox.scene.task.ffp.FixedFunctionRenderTask;
 import com.ferox.scene.task.light.ComputeLightGroupTask;
 import com.ferox.scene.task.light.ComputeShadowFrustumTask;
-import com.ferox.util.geom.Box;
-import com.ferox.util.geom.Geometry;
-import com.ferox.util.geom.Sphere;
-import com.ferox.util.geom.Teapot;
 import com.ferox.util.profile.Profiler;
 import com.lhkbob.entreri.ComponentData;
 import com.lhkbob.entreri.Entity;

ferox-demos/src/main/java/com/ferox/scene/controller/ffp/TransparentDemo.java

 import com.ferox.renderer.OnscreenSurface;
 import com.ferox.renderer.Renderer.DrawStyle;
 import com.ferox.renderer.impl.lwjgl.LwjglFramework;
+import com.ferox.resource.geom.Box;
+import com.ferox.resource.geom.Geometry;
 import com.ferox.scene.AmbientLight;
 import com.ferox.scene.BlinnPhongMaterial;
 import com.ferox.scene.Camera;
 import com.ferox.scene.task.light.ComputeLightGroupTask;
 import com.ferox.scene.task.light.ComputeShadowFrustumTask;
 import com.ferox.util.ApplicationStub;
-import com.ferox.util.geom.Box;
-import com.ferox.util.geom.Geometry;
 import com.ferox.util.profile.Profiler;
 import com.lhkbob.entreri.Entity;
 import com.lhkbob.entreri.EntitySystem;

ferox-demos/src/main/java/com/ferox/util/ApplicationStub.java

 import com.ferox.renderer.Framework;
 import com.ferox.renderer.OnscreenSurface;
 import com.ferox.renderer.OnscreenSurfaceOptions;
-import com.ferox.util.geom.text.CharacterSet;
-import com.ferox.util.geom.text.TextRenderer;
-import com.ferox.util.geom.text.TextRenderer.Anchor;
+import com.ferox.resource.geom.text.CharacterSet;
+import com.ferox.resource.geom.text.TextRenderer;
+import com.ferox.resource.geom.text.TextRenderer.Anchor;
 
 public abstract class ApplicationStub {
     private final Framework framework;

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/geom/Box.java

+/*
+ * Ferox, a graphics and game library in Java
+ *
+ * Copyright (c) 2012, Michael Ludwig
+ * 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.
+ *
+ * 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 HOLDER 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.ferox.resource.geom;
+
+import com.ferox.math.AxisAlignedBox;
+import com.ferox.math.Const;
+import com.ferox.math.Vector3;
+import com.ferox.renderer.Renderer.PolygonType;
+import com.ferox.resource.BufferData;
+import com.ferox.resource.VertexAttribute;
+import com.ferox.resource.VertexBufferObject;
+import com.ferox.resource.VertexBufferObject.StorageMode;
+
+/**
+ * <p>
+ * Box contains factory methods for creating 6-sided rectangular prism
+ * Geometries.
+ * 
+ * @author Michael Ludwig
+ */
+public final class Box {
+    private Box() {}
+
+    /**
+     * Construct a box centered on its origin, with the given side length. So,
+     * Box(1f) creates a unit cube. Uses StorageMode.IN_MEMORY for its
+     * VertexBufferObjects.
+     * 
+     * @param side The side length of the created cube
+     * @return The new geometry
+     * @throws IllegalArgumentException if side is negative
+     */
+    public static Geometry create(double side) {
+        return create(side, StorageMode.IN_MEMORY);
+    }
+
+    /**
+     * Construct a new Box with the given minimum and maximum points. These
+     * points are opposite corners of the box. Uses StorageMode.IN_MEMORY for
+     * its VertexBufferObjects.
+     * 
+     * @param min Minimum corner of the box
+     * @param max Maximum corner of the box
+     * @return The new geometry
+     * @throws NullPointerException if min or max are null
+     * @throws IllegalArgumentException if min has any coordinate less than the
+     *             corresponding coordinate of max
+     */
+    public static Geometry create(@Const Vector3 min, @Const Vector3 max) {
+        return create(min, max, StorageMode.IN_MEMORY);
+    }
+
+    /**
+     * Construct a box centered on its origin, with the given side length. So,
+     * Box(1.0) creates a unit cube.
+     * 
+     * @param side The side length of the created cube
+     * @param mode The storage mode to use for the Box
+     * @return The new geometry
+     * @throws NullPointerException if mode is null
+     * @throws IllegalArgumentException if side is negative
+     */
+    public static Geometry create(double side, StorageMode mode) {
+        return create(new Vector3(-side / 2, -side / 2, -side / 2),
+                      new Vector3(side / 2, side / 2, side / 2), mode);
+    }
+
+    /**
+     * Construct a box centered on its origin, with the given side lengths along
+     * each local axis.
+     * 
+     * @param xExtent The side length along the x axis
+     * @param yExtent The side length along the y axis
+     * @param zExtent The side length along the z axis
+     * @param mode The storage mode
+     * @return The new geometry
+     * @throws NullPointerException if mode is null
+     * @throws IllegalMonitorStateException if any dimension is negative
+     */
+    public static Geometry create(double xExtent, double yExtent, double zExtent,
+                                  StorageMode mode) {
+        return create(new Vector3(-xExtent / 2, -yExtent / 2, -zExtent / 2),
+                      new Vector3(xExtent / 2, yExtent / 2, zExtent / 2), mode);
+    }
+
+    /**
+     * Construct a new Box with the given minimum and maximum points. These
+     * points are opposite corners of the box.
+     * 
+     * @param min Minimum corner of the box
+     * @param max Maximum corner of the box
+     * @param mode The compile type to use for the Box
+     * @return The new geometry
+     * @throws NullPointerException if min, max or mode are null
+     * @throws IllegalArgumentException if min has any coordinate less than the
+     *             corresponding coordinate of max
+     */
+    public static Geometry create(@Const Vector3 min, @Const Vector3 max, StorageMode mode) {
+        return new BoxImpl(min, max, mode);
+    }
+
+    private static class BoxImpl implements Geometry {
+        // Holds vertices, normals, texture coordinates packed as V3F_N3F_T2F
+        // ordered in such a way as to not need indices
+        private final VertexBufferObject vertexAttributes;
+
+        private final VertexAttribute vertices;
+        private final VertexAttribute normals;
+        private final VertexAttribute texCoords;
+
+        private final AxisAlignedBox bounds;
+
+        public BoxImpl(@Const Vector3 min, @Const Vector3 max, StorageMode mode) {
+
+            if (min == null || max == null) {
+                throw new NullPointerException("Min and max vectors cannot be null");
+            }
+            if (mode == null) {
+                throw new NullPointerException("StorageMode cannot be null");
+            }
+
+            if (min.x > max.x || min.y > max.y || min.z > max.z) {
+                throw new IllegalArgumentException("Min vertex has coordinate greater than 'max': " + min + " - " + max);
+            }
+
+            float maxX = (float) max.x;
+            float maxY = (float) max.y;
+            float maxZ = (float) max.z;
+            float minX = (float) min.x;
+            float minY = (float) min.y;
+            float minZ = (float) min.z;
+
+            int i = 0;
+            float[] va = new float[192]; // 72v + 72n + 48t
+
+            // back
+            /* v */va[i++] = minX;
+            va[i++] = maxY;
+            va[i++] = minZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            va[i++] = -1f; /* t */
+            va[i++] = 1f;
+            va[i++] = 1f;
+            /* v */va[i++] = maxX;
+            va[i++] = maxY;
+            va[i++] = minZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            va[i++] = -1f; /* t */
+            va[i++] = 0f;
+            va[i++] = 1f;
+            /* v */va[i++] = maxX;
+            va[i++] = minY;
+            va[i++] = minZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            va[i++] = -1f; /* t */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            /* v */va[i++] = minX;
+            va[i++] = minY;
+            va[i++] = minZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            va[i++] = -1f; /* t */
+            va[i++] = 1f;
+            va[i++] = 0f;
+
+            // right
+            /* v */va[i++] = maxX;
+            va[i++] = maxY;
+            va[i++] = minZ; /* n */
+            va[i++] = 1f;
+            va[i++] = 0f;
+            va[i++] = 0f; /* t */
+            va[i++] = 1f;
+            va[i++] = 1f;
+            /* v */va[i++] = maxX;
+            va[i++] = maxY;
+            va[i++] = maxZ; /* n */
+            va[i++] = 1f;
+            va[i++] = 0f;
+            va[i++] = 0f; /* t */
+            va[i++] = 0f;
+            va[i++] = 1f;
+            /* v */va[i++] = maxX;
+            va[i++] = minY;
+            va[i++] = maxZ; /* n */
+            va[i++] = 1f;
+            va[i++] = 0f;
+            va[i++] = 0f; /* t */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            /* v */va[i++] = maxX;
+            va[i++] = minY;
+            va[i++] = minZ; /* n */
+            va[i++] = 1f;
+            va[i++] = 0f;
+            va[i++] = 0f; /* t */
+            va[i++] = 1f;
+            va[i++] = 0f;
+
+            // front
+            /* v */va[i++] = maxX;
+            va[i++] = maxY;
+            va[i++] = maxZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            va[i++] = 1f; /* t */
+            va[i++] = 1f;
+            va[i++] = 1f;
+            /* v */va[i++] = minX;
+            va[i++] = maxY;
+            va[i++] = maxZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            va[i++] = 1f; /* t */
+            va[i++] = 0f;
+            va[i++] = 1f;
+            /* v */va[i++] = minX;
+            va[i++] = minY;
+            va[i++] = maxZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            va[i++] = 1f; /* t */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            /* v */va[i++] = maxX;
+            va[i++] = minY;
+            va[i++] = maxZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            va[i++] = 1f; /* t */
+            va[i++] = 1f;
+            va[i++] = 0f;
+
+            // left
+            /* v */va[i++] = minX;
+            va[i++] = maxY;
+            va[i++] = maxZ; /* n */
+            va[i++] = -1f;
+            va[i++] = 0f;
+            va[i++] = 0f; /* t */
+            va[i++] = 1f;
+            va[i++] = 1f;
+            /* v */va[i++] = minX;
+            va[i++] = maxY;
+            va[i++] = minZ; /* n */
+            va[i++] = -1f;
+            va[i++] = 0f;
+            va[i++] = 0f; /* t */
+            va[i++] = 0f;
+            va[i++] = 1f;
+            /* v */va[i++] = minX;
+            va[i++] = minY;
+            va[i++] = minZ; /* n */
+            va[i++] = -1f;
+            va[i++] = 0f;
+            va[i++] = 0f; /* t */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            /* v */va[i++] = minX;
+            va[i++] = minY;
+            va[i++] = maxZ; /* n */
+            va[i++] = -1f;
+            va[i++] = 0f;
+            va[i++] = 0f; /* t */
+            va[i++] = 1f;
+            va[i++] = 0f;
+
+            // top
+            /* v */va[i++] = maxX;
+            va[i++] = maxY;
+            va[i++] = minZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 1f;
+            va[i++] = 0f; /* t */
+            va[i++] = 1f;
+            va[i++] = 1f;
+            /* v */va[i++] = minX;
+            va[i++] = maxY;
+            va[i++] = minZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 1f;
+            va[i++] = 0f; /* t */
+            va[i++] = 0f;
+            va[i++] = 1f;
+            /* v */va[i++] = minX;
+            va[i++] = maxY;
+            va[i++] = maxZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 1f;
+            va[i++] = 0f; /* t */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            /* v */va[i++] = maxX;
+            va[i++] = maxY;
+            va[i++] = maxZ; /* n */
+            va[i++] = 0f;
+            va[i++] = 1f;
+            va[i++] = 0f; /* t */
+            va[i++] = 1f;
+            va[i++] = 0f;
+
+            // bottom
+            /* v */va[i++] = minX;
+            va[i++] = minY;
+            va[i++] = minZ; /* n */
+            va[i++] = 0f;
+            va[i++] = -1f;
+            va[i++] = 0f; /* t */
+            va[i++] = 1f;
+            va[i++] = 1f;
+            /* v */va[i++] = maxX;
+            va[i++] = minY;
+            va[i++] = minZ; /* n */
+            va[i++] = 0f;
+            va[i++] = -1f;
+            va[i++] = 0f; /* t */
+            va[i++] = 0f;
+            va[i++] = 1f;
+            /* v */va[i++] = maxX;
+            va[i++] = minY;
+            va[i++] = maxZ; /* n */
+            va[i++] = 0f;
+            va[i++] = -1f;
+            va[i++] = 0f; /* t */
+            va[i++] = 0f;
+            va[i++] = 0f;
+            /* v */va[i++] = minX;
+            va[i++] = minY;
+            va[i++] = maxZ; /* n */
+            va[i++] = 0f;
+            va[i++] = -1f;
+            va[i++] = 0f; /* t */
+            va[i++] = 1f;
+            va[i++] = 0f;
+
+            vertexAttributes = new VertexBufferObject(new BufferData(va), mode);
+            vertices = new VertexAttribute(vertexAttributes, 3, 0, 5);
+            normals = new VertexAttribute(vertexAttributes, 3, 3, 5);
+            texCoords = new VertexAttribute(vertexAttributes, 2, 6, 6);
+
+            bounds = new AxisAlignedBox(new Vector3(minX, minY, minZ), new Vector3(maxX,
+                                                                                   maxY,
+                                                                                   maxZ));
+        }
+
+        @Override
+        public PolygonType getPolygonType() {
+            return PolygonType.QUADS;
+        }
+
+        @Override
+        public VertexBufferObject getIndices() {
+            return null;
+        }
+
+        @Override
+        public int getIndexOffset() {
+            return 0;
+        }
+
+        @Override
+        public int getIndexCount() {
+            return 24;
+        }
+
+        @Override
+        public VertexAttribute getVertices() {
+            return vertices;
+        }
+
+        @Override
+        public VertexAttribute getNormals() {
+            return normals;
+        }
+
+        @Override
+        public VertexAttribute getTextureCoordinates() {
+            return texCoords;
+        }
+
+        @Override
+        public VertexAttribute getTangents() {
+            throw new UnsupportedOperationException("NOT IMPLEMENTED YET");
+        }
+
+        @Override
+        @Const
+        public AxisAlignedBox getBounds() {
+            return bounds;
+        }
+    }
+}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/geom/Cylinder.java

+/*
+ * Ferox, a graphics and game library in Java
+ *
+ * Copyright (c) 2012, Michael Ludwig
+ * 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.
+ *
+ * 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 HOLDER 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.ferox.resource.geom;
+
+import com.ferox.math.AxisAlignedBox;
+import com.ferox.math.Const;
+import com.ferox.math.Matrix3;
+import com.ferox.math.Vector3;
+import com.ferox.renderer.Renderer.PolygonType;
+import com.ferox.resource.BufferData;
+import com.ferox.resource.VertexAttribute;
+import com.ferox.resource.VertexBufferObject;
+import com.ferox.resource.VertexBufferObject.StorageMode;
+
+/**
+ * <p>
+ * Cylinder contains factory methods for creating approximations of an ideal
+ * cylinder with a configurable radius and height. The accuracy of the
+ * approximation depends on a parameter termed <tt>resolution</tt>, which
+ * represents the number of samples along the circular caps.
+ * 
+ * @author Michael Ludwig
+ */
+public class Cylinder {
+    // we need a float PI since we're building float vertices
+    private static final float PI = (float) Math.PI;
+
+    private Cylinder() {}
+
+    /**
+     * Create a new Cylinder with the given radius and height, a resolution of
+     * 8, and a StorageMode of IN_MEMORY. Its axis will be the positive y-axis.
+     * 
+     * @param radius The radius of the cylinder, in local space
+     * @param height The height of the cylinder
+     * @return The new geometry
+     * @throws IllegalArgumentException if radius <= 0
+     */
+    public static Geometry create(double radius, double height) {
+        return create(radius, height, 8);
+    }
+
+    /**
+     * Create a new Cylinder with the given radius, height, and resolution. It
+     * uses a StorageMode of IN_MEMORY. Its axis will be the positive y-axis.
+     * 
+     * @param radius The radius of the cylinder, in local space
+     * @param height The height of the cylinder
+     * @param res The resolution of the cylinder, the higher the value the
+     *            smoother the tesselation
+     * @return The new geometry
+     * @throws IllegalArgumentException if radius <= 0 or if res < 4
+     */
+    public static Geometry create(double radius, double height, int res) {
+        return create(radius, height, res, StorageMode.IN_MEMORY);
+    }
+
+    /**
+     * Create a new Cylinder with the given radius, height and StorageMode. It
+     * uses a resolution of 8. Its axis will be the positive y-axis.
+     * 
+     * @param radius The radius of the cylinder, in local space
+     * @param height The height of the cylinder
+     * @param mode The StorageMode to use
+     * @return The new geometry
+     * @throws IllegalArgumentException if radius <= 0
+     * @throws NullPointerException if mode is null
+     */
+    public static Geometry create(double radius, double height, StorageMode mode) {
+        return create(radius, height, 8, mode);
+    }
+
+    /**
+     * Create a new cylinder with the given radius, height, resolution and
+     * StorageMode. Its axis will be the positive y-axis.
+     * 
+     * @param radius The radius of the cylinder, in local space
+     * @param height The height of the cylinder
+     * @param res The resolution of the sphere
+     * @param mode The StorageMode to use
+     * @return The new geometry
+     * @throws IllegalArgumentException if radius <= 0 or if res < 4
+     * @throws NullPointerException if mode is null
+     */
+    public static Geometry create(double radius, double height, int res, StorageMode mode) {
+        return create(new Vector3(0, 1, 0), radius, height, res, mode);
+    }
+
+    /**
+     * Create a new cylinder with the given vertical axis, radius, height,
+     * resolution and StorageMode.
+     * 
+     * @param axis The vertical axis of the cylinder
+     * @param radius The radius of the cylinder, in local space
+     * @param height The height of the cylinder
+     * @param res The resolution of the sphere
+     * @param mode The StorageMode to use
+     * @return The new geometry
+     * @throws IllegalArgumentException if radius <= 0 or if res < 4
+     * @throws NullPointerException if mode is null
+     */
+    public static Geometry create(@Const Vector3 axis, double radius, double height,
+                                  int res, StorageMode mode) {
+        return new CylinderImpl(axis, radius, height, res, mode);
+    }
+
+    private static class CylinderImpl implements Geometry {
+        // Holds vertices, normals, texture coordinates packed as V3F_N3F_T2F
+        private final VertexBufferObject vertexAttributes;
+
+        private final VertexAttribute vertices;
+        private final VertexAttribute normals;
+        private final VertexAttribute texCoords;
+
+        private final VertexBufferObject indices;
+
+        private final AxisAlignedBox bounds;
+
+        public CylinderImpl(@Const Vector3 axis, double radius, double height, int res,
+                            StorageMode mode) {
+            if (radius <= 0) {
+                throw new IllegalArgumentException("Invalid radius, must be > 0, not: " + radius);
+            }
+            if (height <= 0) {
+                throw new IllegalArgumentException("Invalid height, must be > 0, not: " + height);
+            }
+            if (res < 4) {
+                throw new IllegalArgumentException("Invalid resolution, must be > 3, not: " + res);
+            }
+            if (mode == null) {
+                throw new NullPointerException("StorageMode cannot be null");
+            }
+
+            // compute cache for rings
+            float[] xCoord = new float[res + 1];
+            float[] zCoord = new float[res + 1];
+            float[] uCoord = new float[res + 1];
+
+            float xzAngle = 0;
+            float dXZ = 2 * PI / res;
+            for (int i = 0; i < res; i++) {
+                xCoord[i] = (float) (radius * Math.cos(xzAngle));
+                zCoord[i] = (float) (radius * Math.sin(xzAngle));
+                uCoord[i] = (float) i / res;
+                xzAngle += dXZ;
+            }
+
+            // wrap around, but with an updated texture coordinate
+            xCoord[res] = xCoord[0];
+            zCoord[res] = zCoord[0];
+            uCoord[res] = 1;
+
+            // vertices required are two rings with normals facing up/down for the cap,
+            // another two rings with normals facing out from the tube, and duplicated
+            // center points (for different TCs) in the center of the caps
+            int vertexCount = 6 * (res + 1);
+
+            float[] va = new float[vertexCount * 8]; // 3v + 3n + 2tc
+            int[] indices = new int[18 * res];
+
+            int vi = 0;
+            int ii = 0;
+
+            // first cap
+            for (int i = 0; i <= res; i++) {
+                // outer point
+                va[vi++] = xCoord[i]; // vx
+                va[vi++] = (float) (.5 * height); // vy
+                va[vi++] = zCoord[i]; // vz
+
+                va[vi++] = 0; // nx
+                va[vi++] = 1; // ny
+                va[vi++] = 0; // nz
+
+                va[vi++] = uCoord[i]; // tx
+                va[vi++] = 1; // ty
+
+                // center
+                va[vi++] = 0; // vx
+                va[vi++] = (float) (.5 * height); // vy
+                va[vi++] = 0; // vz
+
+                va[vi++] = 0; // nx
+                va[vi++] = 1; // ny
+                va[vi++] = 0; // nz
+
+                va[vi++] = uCoord[i]; // tx
+                va[vi++] = 1; // ty
+
+                if (i != res) {
+                    // form triangle with proper winding
+                    indices[ii++] = i * 2;
+                    indices[ii++] = i * 2 + 1;
+                    indices[ii++] = i * 2 + 2;
+                }
+            }
+
+            // second cap
+            int offset = vi / 8;
+            for (int i = 0; i <= res; i++) {
+                // outer point
+                va[vi++] = xCoord[i]; // vx
+                va[vi++] = (float) (-.5 * height); // vy
+                va[vi++] = zCoord[i]; // vz
+
+                va[vi++] = 0; // nx
+                va[vi++] = -1; // ny
+                va[vi++] = 0; // nz
+
+                va[vi++] = uCoord[i]; // tx
+                va[vi++] = 0; // ty
+
+                // center
+                va[vi++] = 0; // vx
+                va[vi++] = (float) (-.5 * height); // vy
+                va[vi++] = 0; // vz
+
+                va[vi++] = 0; // nx
+                va[vi++] = -1; // ny
+                va[vi++] = 0; // nz
+
+                va[vi++] = uCoord[i]; // tx
+                va[vi++] = 0; // ty
+
+                if (i != res) {
+                    // form a triangle with proper winding
+                    indices[ii++] = offset + i * 2;
+                    indices[ii++] = offset + i * 2 + 2;
+                    indices[ii++] = offset + i * 2 + 1;
+                }
+            }
+
+            // tube
+            offset = vi / 8;
+            for (int i = 0; i <= res; i++) {
+                // place two vertices in panel
+                va[vi++] = xCoord[i];
+                va[vi++] = (float) (.5 * height);
+                va[vi++] = zCoord[i];
+
+                va[vi++] = xCoord[i];
+                va[vi++] = 0;
+                va[vi++] = zCoord[i];
+
+                va[vi++] = uCoord[i];
+                va[vi++] = 1;
+
+                va[vi++] = xCoord[i];
+                va[vi++] = (float) (-.5 * height);
+                va[vi++] = zCoord[i];
+
+                va[vi++] = xCoord[i];
+                va[vi++] = 0;
+                va[vi++] = zCoord[i];
+
+                va[vi++] = uCoord[i];
+                va[vi++] = 0;
+
+                if (i != res) {
+                    // form two triangles with proper winding
+                    indices[ii++] = offset + i * 2;
+                    indices[ii++] = offset + i * 2 + 2; // (i + 1) * 2
+                    indices[ii++] = offset + i * 2 + 1;
+
+                    indices[ii++] = offset + i * 2 + 2; // (i + 1) * 2 + 1
+                    indices[ii++] = offset + i * 2 + 3;
+                    indices[ii++] = offset + i * 2 + 1;
+                }
+            }
+
+            // now take into account the requested axis
+            Vector3 u = new Vector3(1, 0, 0);
+            Vector3 v = new Vector3().normalize(axis);
+            Matrix3 m = new Matrix3();
+
+            if (axis.equals(u)) {
+                // update it to get the right ortho-normal basis
+                u.set(0, -1, 0);
+            } else {
+                // compute orthogonal x-axis in the direction of (1, 0, 0)
+                u.ortho(axis);
+            }
+
+            m.setCol(0, u);
+            m.setCol(1, v);
+            m.setCol(2, u.cross(v).normalize());
+
+            Matrix3 n = new Matrix3(m).inverse();
+
+            for (int i = 0; i < va.length; i += 8) {
+                // vertex
+                u.set(va, i);
+                u.mul(m, u);
+                u.get(va, i);
+
+                // normal
+                u.set(va, i + 3);
+                u.mul(u, n);
+                u.get(va, i + 3);
+            }
+
+            this.indices = new VertexBufferObject(new BufferData(indices), mode);
+            vertexAttributes = new VertexBufferObject(new BufferData(va), mode);
+            vertices = new VertexAttribute(vertexAttributes, 3, 0, 5);
+            normals = new VertexAttribute(vertexAttributes, 3, 3, 5);
+            texCoords = new VertexAttribute(vertexAttributes, 2, 6, 6);
+
+            bounds = new AxisAlignedBox(new Vector3(-radius, -height, -radius),
+                                        new Vector3(radius, height, radius));
+        }
+
+        @Override
+        public PolygonType getPolygonType() {
+            return PolygonType.TRIANGLES;
+        }
+
+        @Override
+        public VertexBufferObject getIndices() {
+            return indices;
+        }
+
+        @Override
+        public int getIndexOffset() {
+            return 0;
+        }
+
+        @Override
+        public int getIndexCount() {
+            return indices.getData().getLength();
+        }
+
+        @Override
+        public VertexAttribute getVertices() {
+            return vertices;
+        }
+
+        @Override
+        public VertexAttribute getNormals() {
+            return normals;
+        }
+
+        @Override
+        public VertexAttribute getTextureCoordinates() {
+            return texCoords;
+        }
+
+        @Override
+        public VertexAttribute getTangents() {
+            throw new UnsupportedOperationException("NOT IMPLEMENTED YET");
+        }
+
+        @Override
+        @Const
+        public AxisAlignedBox getBounds() {
+            return bounds;
+        }
+    }
+}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/geom/Geometry.java

+/*
+ * Ferox, a graphics and game library in Java
+ *
+ * Copyright (c) 2012, Michael Ludwig
+ * 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.
+ *
+ * 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 HOLDER 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.ferox.resource.geom;
+
+import com.ferox.math.AxisAlignedBox;
+import com.ferox.math.Const;
+import com.ferox.renderer.Renderer.PolygonType;
+import com.ferox.resource.VertexAttribute;
+import com.ferox.resource.VertexBufferObject;
+
+/**
+ * <p>
+ * Geometry is utility provider of indices and vertex attributes to represent
+ * renderable shapes. The geometry information should be considered immutable
+ * unless something else edits them. Geometry implementations will provide the
+ * geometry but will not modify it.
+ * </p>
+ * 
+ * @author Michael Ludwig
+ */
+public interface Geometry {
+    /**
+     * Return a reasonably tight-fitting bounds over the vertices of this
+     * Geometry. The returned bounds should not be modified.
+     * 
+     * @return The bounds of the geometry in its local coordinate system
+     */
+    public @Const
+    AxisAlignedBox getBounds();
+
+    /**
+     * Return the polygon type that determines how consecutive vertex elements
+     * or indices are converted into polygons.
+     * 
+     * @return The polygon type
+     */
+    public PolygonType getPolygonType();
+
+    /**
+     * <p>
+     * Return a VertexBufferObject containing the index information used to
+     * access the vertex attributes of the geometry. Each index selects an
+     * element from each of the attributes of the geometry. Consecutive vertices
+     * are then combined into polygons based on the {@link #getPolygonType()
+     * polygon type} of the geometry.
+     * </p>
+     * <p>
+     * This can return null if the geometry can be rendered by processing the
+     * vertex elements consecutively. See {@link #getIndexCount()} and
+     * {@link #getIndexOffset()} for how they are interpreted when the indices
+     * are null.
+     * </p>
+     * 
+     * @return The indices of the geometry, must have a data type that is not
+     *         FLOAT if not null
+     */
+    public VertexBufferObject getIndices();
+
+    /**
+     * Return the offset into the indices before the first index is read. If
+     * {@link #getIndices()} returns null, it is the vertex offset before the
+     * first vertex is rendered.
+     * 
+     * @return The index offset before rendering is started
+     */
+    public int getIndexOffset();
+
+    /**
+     * Return the number of indices to render, or if {@link #getIndices()}
+     * returns null, it is the number of vertex elements to render.
+     * 
+     * @return The number of indices or elements to render
+     */
+    public int getIndexCount();
+
+    /**
+     * Return a VertexAttribute containing position vector information
+     * describing the geometry. The exact layout within the VertexAttribute is
+     * left to the implementation, including the element size of the position
+     * elements.
+     * 
+     * @return The vertices for the geometry, cannot be null
+     */
+    public VertexAttribute getVertices();
+
+    /**
+     * Return the VertexAttribute containing normal vector information
+     * associated with each vertex returned by {@link #getVertices()}. The exact
+     * layout within the returned VertexAttribute is left to the implementation
+     * (it may even use the same VertexBufferObject as the vertices and other
+     * attributes), but its element size must be 3.
+     * 
+     * @return The normals for the geometry, cannot be null
+     */
+    public VertexAttribute getNormals();
+
+    /**
+     * Return the VertexAttribute containing texture coordinates associated with
+     * each vertex returned by {@link #getVertices()}. The exact layout within
+     * the attribute is left to the implementation. It is assumed that the
+     * texture coordinates represent the logical unwrapping of the surface so
+     * the element size should be 2.
+     * 
+     * @return The 2D texture coordinates for the geometry, cannot be null
+     */
+    public VertexAttribute getTextureCoordinates();
+
+    /**
+     * Return the VertexAttribute containing the tangent vectors for the
+     * surface. The tangent vectors are orthogonal to the normal vectors at each
+     * vertex and form the tangent space of the geometry. It is most commonly
+     * used for normal mapping light techniques. It is assumed that the element
+     * size is 3.
+     * 
+     * @return The tangent vectors for the geometry, cannot be null
+     */
+    public VertexAttribute getTangents();
+}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/geom/Rectangle.java

+/*
+ * Ferox, a graphics and game library in Java
+ *
+ * Copyright (c) 2012, Michael Ludwig
+ * 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.
+ *
+ * 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 HOLDER 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.ferox.resource.geom;
+
+import com.ferox.math.AxisAlignedBox;
+import com.ferox.math.Const;
+import com.ferox.math.Vector3;
+import com.ferox.renderer.Renderer.PolygonType;
+import com.ferox.resource.BufferData;
+import com.ferox.resource.VertexAttribute;
+import com.ferox.resource.VertexBufferObject;
+import com.ferox.resource.VertexBufferObject.StorageMode;
+
+/**
+ * <p>
+ * Rectangle contains factory methods to create a single quad aligned with a
+ * specified x and y axis, in three dimensions. It is very useful for fullscreen
+ * effects that require rendering a rectangle across the entire screen.
+ * 
+ * @author Michael Ludwig
+ */
+public final class Rectangle {
+    private Rectangle() {}
+
+    /**
+     * Create a Rectangle with an x basis vector of (1, 0, 0) and a y basis
+     * vector of (0, 1, 0), and the given edge dimensions. The storage mode is
+     * IN_MEMORY.
+     * 
+     * @param left The left edge of the rectangle
+     * @param right The right edge of the rectangle
+     * @param bottom The bottom edge of the rectangle
+     * @param top The top edge of the rectangle
+     * @return The new geometry
+     * @throws IllegalArgumentException if left > right or bottom > top
+     */
+    public static Geometry create(double left, double right, double bottom, double top) {
+        return create(left, right, bottom, top, new Vector3(1f, 0f, 0f), new Vector3(0f,
+                                                                                     1f,
+                                                                                     0f));
+    }
+
+    /**
+     * Create a Rectangle with the given basis vectors and edge dimensions and a
+     * storage mode of IN_MEMORY.
+     * 
+     * 
+     * @param left The left edge of the rectangle
+     * @param right The right edge of the rectangle
+     * @param bottom The bottom edge of the rectangle
+     * @param top The top edge of the rectangle
+     * @param xAxis Local x-axis of the rectangle
+     * @param yAxis Local y-axis of the rectangle
+     * @return The new geometry
+     * @throws IllegalArgumentException if left > right or bottom > top
+     * @throws NullPointerException if xAxis or yAxis are null
+     */
+    public static Geometry create(double left, double right, double bottom, double top,
+                                  @Const Vector3 xAxis, @Const Vector3 yAxis) {
+        return create(left, right, bottom, top, xAxis, yAxis, StorageMode.IN_MEMORY);
+    }
+
+    /**
+     * Create a Rectangle with the given basis vectors, edge dimensions and
+     * storage mode
+     * 
+     * @param xAxis
+     * @param yAxis
+     * @param left
+     * @param right
+     * @param bottom
+     * @param top
+     * @param mode The storage mode to use
+     * @return The new geometry
+     * @throws IllegalArgumentException if left > right or bottom > top
+     * @throws NullPointerException if xAxis, yAxis, or mode are null
+     */
+    public static Geometry create(double left, double right, double bottom, double top,
+                                  @Const Vector3 xAxis, @Const Vector3 yAxis,
+                                  StorageMode mode) {
+        return new RectangleImpl(left, right, bottom, top, xAxis, yAxis, mode);
+    }
+
+    private static class RectangleImpl implements Geometry {
+        // Holds vertices, normals, texture coordinates packed as V3F_N3F_T2F
+        // ordered in such a way as to not need indices
+        private final VertexBufferObject vertexAttributes;
+
+        private final VertexAttribute vertices;
+        private final VertexAttribute normals;
+        private final VertexAttribute texCoords;
+
+        private final AxisAlignedBox bounds;
+
+        public RectangleImpl(double left, double right, double bottom, double top,
+                             @Const Vector3 xAxis, @Const Vector3 yAxis, StorageMode mode) {
+            if (left > right || bottom > top) {
+                throw new IllegalArgumentException("Side positions of the square are incorrect");
+            }
+            if (xAxis == null || yAxis == null) {
+                throw new NullPointerException("Axis cannot be null");
+            }
+            if (mode == null) {
+                throw new NullPointerException("StorageMode cannot be null");
+            }
+
+            Vector3 normal = new Vector3().cross(xAxis, yAxis);
+
+            float[] va = new float[32];
+            int i = 0;
+
+            // lower-left
+            va[i++] = (float) (xAxis.x * left + yAxis.x * bottom);
+            va[i++] = (float) (xAxis.y * left + yAxis.y * bottom);
+            va[i++] = (float) (xAxis.z * left + yAxis.z * bottom);
+
+            va[i++] = (float) normal.x;
+            va[i++] = (float) normal.y;
+            va[i++] = (float) normal.z;
+
+            va[i++] = 0f;
+            va[i++] = 0f;
+
+            // lower-right
+            va[i++] = (float) (xAxis.x * right + yAxis.x * bottom);
+            va[i++] = (float) (xAxis.y * right + yAxis.y * bottom);
+            va[i++] = (float) (xAxis.z * right + yAxis.z * bottom);
+
+            va[i++] = (float) normal.x;
+            va[i++] = (float) normal.y;
+            va[i++] = (float) normal.z;
+
+            va[i++] = 1f;
+            va[i++] = 0f;
+
+            // upper-right
+            va[i++] = (float) (xAxis.x * right + yAxis.x * top);
+            va[i++] = (float) (xAxis.y * right + yAxis.y * top);
+            va[i++] = (float) (xAxis.z * right + yAxis.z * top);
+
+            va[i++] = (float) normal.x;
+            va[i++] = (float) normal.y;
+            va[i++] = (float) normal.z;
+
+            va[i++] = 1f;
+            va[i++] = 1f;
+
+            // upper-left
+            va[i++] = (float) (xAxis.x * left + yAxis.x * top);
+            va[i++] = (float) (xAxis.y * left + yAxis.y * top);
+            va[i++] = (float) (xAxis.z * left + yAxis.z * top);
+
+            va[i++] = (float) normal.x;
+            va[i++] = (float) normal.y;
+            va[i++] = (float) normal.z;
+
+            va[i++] = 0f;
+            va[i++] = 1f;
+
+            vertexAttributes = new VertexBufferObject(new BufferData(va), mode);
+            vertices = new VertexAttribute(vertexAttributes, 3, 0, 5);
+            normals = new VertexAttribute(vertexAttributes, 3, 3, 5);
+            texCoords = new VertexAttribute(vertexAttributes, 2, 6, 6);
+
+            bounds = new AxisAlignedBox(va, 0, 5, 4);
+        }
+
+        @Override
+        public PolygonType getPolygonType() {
+            return PolygonType.QUADS;
+        }
+
+        @Override
+        public VertexBufferObject getIndices() {
+            return null;
+        }
+
+        @Override
+        public int getIndexOffset() {
+            return 0;
+        }
+
+        @Override
+        public int getIndexCount() {
+            return 4;
+        }
+
+        @Override
+        public VertexAttribute getVertices() {
+            return vertices;
+        }
+
+        @Override
+        public VertexAttribute getNormals() {
+            return normals;
+        }
+
+        @Override
+        public VertexAttribute getTextureCoordinates() {
+            return texCoords;
+        }
+
+        @Override
+        public VertexAttribute getTangents() {
+            throw new UnsupportedOperationException("NOT IMPLEMENTED YET");
+        }
+
+        @Override
+        @Const
+        public AxisAlignedBox getBounds() {
+            return bounds;
+        }
+    }
+}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/geom/Sphere.java

+/*
+ * Ferox, a graphics and game library in Java
+ *
+ * Copyright (c) 2012, Michael Ludwig
+ * 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.
+ *
+ * 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 HOLDER 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.ferox.resource.geom;
+
+import com.ferox.math.AxisAlignedBox;
+import com.ferox.math.Const;
+import com.ferox.math.Vector3;
+import com.ferox.renderer.Renderer.PolygonType;
+import com.ferox.resource.BufferData;
+import com.ferox.resource.VertexAttribute;
+import com.ferox.resource.VertexBufferObject;
+import com.ferox.resource.VertexBufferObject.StorageMode;
+
+/**
+ * <p>
+ * Sphere contains factory methods for creating approximations of a mathematical
+ * sphere with a configurable radius. The accuracy of the approximation depends
+ * on a parameter termed <tt>resolution</tt>.
+ * <p>
+ * The approximated sphere is constructed by rotating a number of circles in the
+ * XY-plane about the Y-axis. The number of rotations equals the resolution of
+ * the sphere. Each circle is also approximated by a number of points equal to
+ * the resolution.
+ * 
+ * @author Michael Ludwig
+ */
+public final class Sphere {
+    // we need a float PI since we're building float vertices
+    private static final float PI = (float) Math.PI;
+
+    private Sphere() {}
+
+    /**
+     * Create a new Sphere with the given radius, a resolution of 8, and a
+     * StorageMode of IN_MEMORY.
+     * 
+     * @param radius The radius of the sphere, in local space
+     * @return The new geometry
+     * @throws IllegalArgumentException if radius <= 0
+     */
+    public static Geometry create(double radius) {
+        return create(radius, 8);
+    }
+
+    /**
+     * Create a new Sphere with the given radius and resolution. It uses a
+     * StorageMode of IN_MEMORY.
+     * 
+     * @param radius The radius of the sphere, in local space
+     * @param res The resolution of the sphere, the higher the value the
+     *            smoother the tesselation
+     * @return The new geometry
+     * @throws IllegalArgumentException if radius <= 0 or if res < 4
+     */
+    public static Geometry create(double radius, int res) {
+        return create(radius, res, StorageMode.IN_MEMORY);
+    }
+
+    /**
+     * Create a new Sphere with the given radius and StorageMode. It uses a
+     * resolution of 8.
+     * 
+     * @param radius The radius of the sphere, in local space
+     * @param mode The StorageMode to use
+     * @return The new geometry
+     * @throws IllegalArgumentException if radius <= 0
+     * @throws NullPointerException if mode is null
+     */
+    public static Geometry create(double radius, StorageMode mode) {
+        return create(radius, 8, mode);
+    }
+
+    /**
+     * Create a new Sphere with the given radius, resolution and StorageMode.
+     * 
+     * @param radius The radius of the sphere, in local space
+     * @param res The resolution of the sphere
+     * @param mode The StorageMode to use
+     * @return The new geometry
+     * @throws IllegalArgumentException if radius <= 0 or if res < 4
+     * @throws NullPointerException if mode is null
+     */
+    public static Geometry create(double radius, int res, StorageMode mode) {
+        return new SphereImpl(radius, res, mode);
+    }
+
+    private static class SphereImpl implements Geometry {
+        // Holds vertices, normals, texture coordinates packed as V3F_N3F_T2F
+        private final VertexBufferObject vertexAttributes;
+
+        private final VertexAttribute vertices;
+        private final VertexAttribute normals;
+        private final VertexAttribute texCoords;
+
+        private final VertexBufferObject indices;
+
+        private final AxisAlignedBox bounds;
+
+        public SphereImpl(double radius, int res, StorageMode mode) {
+            if (radius <= 0) {
+                throw new IllegalArgumentException("Invalid radius, must be > 0, not: " + radius);
+            }
+            if (res < 4) {
+                throw new IllegalArgumentException("Invalid resolution, must be > 3, not: " + res);
+            }
+            if (mode == null) {
+                throw new NullPointerException("StorageMode cannot be null");
+            }
+
+            int vertexCount = res * (res + 1);
+
+            float[] xCoord = new float[res + 1];
+            float[] zCoord = new float[res + 1];
+            float[] u = new float[res + 1];
+
+            float xzAngle = 0;
+            float dXZ = 2 * PI / res;
+            for (int i = 0; i < res; i++) {
+                // compute cache for slices
+                xCoord[i] = (float) Math.cos(xzAngle);
+                zCoord[i] = (float) Math.sin(xzAngle);
+                u[i] = i / (float) res;
+                xzAngle += dXZ;
+            }
+
+            // wrap around to connect the sphere
+            xCoord[res] = xCoord[0];
+            zCoord[res] = zCoord[0];
+            u[res] = 1f;
+
+            float[] va = new float[vertexCount * 8]; // 3v + 3n + 2tc
+
+            float floatRadius = (float) radius;
+            float yAngle = PI;
+            float dY = -PI / (res - 1);
+            int index = 0;
+            float y, r, tv;
+            for (int dv = 0; dv < res; dv++) {
+                // compute y values, since they're constant for the whole ring
+                y = (float) Math.cos(yAngle);
+                r = (float) Math.sqrt(1 - y * y);
+                tv = (float) dv / (res - 1);
+                yAngle += dY;
+
+                for (int du = 0; du <= res; du++) {
+                    // place vertices, normals and texcoords
+                    va[index++] = floatRadius * r * xCoord[du]; // vx
+                    va[index++] = floatRadius * y; // vy
+                    va[index++] = floatRadius * r * zCoord[du]; // vz
+
+                    va[index++] = r * xCoord[du]; // nx
+                    va[index++] = y; // ny
+                    va[index++] = r * zCoord[du]; // nz
+
+                    va[index++] = u[du]; // tx
+                    va[index++] = tv; // ty
+                }
+            }
+
+            // build up indices
+            int[] indices = new int[(res - 1) * (2 * res + 2)];
+            index = 0;
+            int v1, v2;
+            for (int dv = 0; dv < res - 1; dv++) {
+                v1 = dv * (res + 1);
+                v2 = (dv + 1) * (res + 1);
+
+                // start off the strip
+                indices[index++] = v1++;
+                indices[index++] = v2++;
+                for (int du = 0; du < res; du++) {
+                    indices[index++] = v1++;
+                    indices[index++] = v2++;
+                }
+            }
+
+            this.indices = new VertexBufferObject(new BufferData(indices), mode);
+            vertexAttributes = new VertexBufferObject(new BufferData(va), mode);
+            vertices = new VertexAttribute(vertexAttributes, 3, 0, 5);
+            normals = new VertexAttribute(vertexAttributes, 3, 3, 5);
+            texCoords = new VertexAttribute(vertexAttributes, 2, 6, 6);
+
+            bounds = new AxisAlignedBox(new Vector3(-radius, -radius, -radius),
+                                        new Vector3(radius, radius, radius));
+        }
+
+        @Override
+        public PolygonType getPolygonType() {
+            return PolygonType.TRIANGLE_STRIP;
+        }
+
+        @Override
+        public VertexBufferObject getIndices() {
+            return indices;
+        }
+
+        @Override
+        public int getIndexOffset() {
+            return 0;
+        }
+
+        @Override
+        public int getIndexCount() {
+            return indices.getData().getLength();
+        }
+
+        @Override
+        public VertexAttribute getVertices() {
+            return vertices;
+        }
+
+        @Override
+        public VertexAttribute getNormals() {
+            return normals;
+        }
+
+        @Override
+        public VertexAttribute getTextureCoordinates() {
+            return texCoords;
+        }
+
+        @Override
+        public VertexAttribute getTangents() {
+            throw new UnsupportedOperationException("NOT IMPLEMENTED YET");
+        }
+
+        @Override
+        @Const
+        public AxisAlignedBox getBounds() {
+            return bounds;
+        }
+    }
+}

ferox-renderer/ferox-renderer-api/src/main/java/com/ferox/resource/geom/Teapot.java

+/*
+ * Ferox, a graphics and game library in Java
+ *
+ * Copyright (c) 2012, Michael Ludwig
+ * 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.
+ *
+ * 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 HOLDER 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.ferox.resource.geom;
+
+import java.util.Arrays;
+
+import com.ferox.math.AxisAlignedBox;
+import com.ferox.math.Const;
+import com.ferox.renderer.Renderer.PolygonType;
+import com.ferox.resource.BufferData;
+import com.ferox.resource.VertexAttribute;
+import com.ferox.resource.VertexBufferObject;
+import com.ferox.resource.VertexBufferObject.StorageMode;
+
+/**
+ * <p>
+ * Teapot contains factory methods to create the classical teapot model in
+ * computer graphices. The vertex data is from "http://www.sjbaker.org/teapot/".
+ * 
+ * @author Michael Ludwig
+ */
+public final class Teapot {
+    private Teapot() {}
+
+    /**
+     * Instantiates a new Teapot object with the given scale with a StorageMode
+     * of IN_MEMORY.
+     * 
+     * @param scale The scale factor that affects the size of the teapot
+     * @return The new geometry
+     * @throws IllegalArgumentException if scale is negative
+     */
+    public static Geometry create(double scale) {
+        return create(scale, StorageMode.IN_MEMORY);
+    }
+