Commits

Michael Ludwig committed 26ae8fc

Add a stub application and update all tests to use that.

Comments (0)

Files changed (21)

ferox-physics/pom.xml

             <artifactId>entreri</artifactId>
             <version>${entreri.version}</version>
         </dependency>
+        
+        <dependency>
+            <groupId>com.lhkbob.ferox</groupId>
+            <artifactId>ferox-test</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>com.lhkbob.ferox</groupId>
             <artifactId>ferox-scene</artifactId>
-            <version>0.0.1-SNAPSHOT</version>
+            <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.lhkbob.ferox</groupId>
             <artifactId>ferox-renderer-lwjgl</artifactId>
-            <version>0.0.1-SNAPSHOT</version>
+            <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
     </dependencies>

ferox-physics/src/main/java/com/ferox/physics/collision/algorithm/EPA.java

     
     private static final int[] I1_MAP = new int[] { 1, 2, 0 };
     private static final int[] I2_MAP = new int[] { 2, 0, 1 };
+    
+    public static int numEPA = 0;
 
     public static ClosestPair evaluate(Simplex simplex) {
+        numEPA++;
         if (simplex.getRank() > 1 && simplex.encloseOrigin()) {
             Bag<Face> hull = new Bag<Face>();
             Face f1 = newFace(simplex, 0, 1, 2, hull);
                     simplex.getVertex(j).set(outer.vertices[j]);
                 }
                 
-                // FIXME these do double the number of required subtractions,
-                // also it might be worth considering creating an optimized crossLength()
-                // method that just computes the length of the cross product
                 Vector3 t = new Vector3();
                 double w1 = Util.normal(projection, outer.vertices[1], outer.vertices[2], t).length();
                 double w2 = Util.normal(projection, outer.vertices[2], outer.vertices[0], t).length();
                 double w3 = Util.normal(projection, outer.vertices[0], outer.vertices[1], t).length();
-//                double w1 = new Vector3().sub(outer.vertices[1], projection).cross(new Vector3().sub(outer.vertices[2], projection)).length();
-//                double w2 = new Vector3().sub(outer.vertices[2], projection).cross(new Vector3().sub(outer.vertices[0], projection)).length();
-//                double w3 = new Vector3().sub(outer.vertices[0], projection).cross(new Vector3().sub(outer.vertices[1], projection)).length();
                 
                 double sum = w1 + w2 + w3;
                 simplex.setWeight(0, w1 / sum);

ferox-physics/src/main/java/com/ferox/physics/collision/algorithm/GJK.java

     private static final double GJK_MIN_DISTANCE = .00001;
     private static final double GJK_DUPLICATE_EPS = .0001;
     private static final double GJK_ACCURACY = .00001;
+    
+    public static int numGJK = 0;
 
     public static Simplex evaluate(MinkowskiShape shape, @Const Vector3 guess) {
+        numGJK++;
+        
         Simplex simplex = new Simplex(shape);
         Vector3 ray = new Vector3(guess);
         if (ray.lengthSquared() < GJK_MIN_DISTANCE * GJK_MIN_DISTANCE)

ferox-physics/src/main/java/com/ferox/physics/collision/algorithm/Util.java

         
         if (result == null)
             result = new Vector3();
+        
+        // compute the cross-product of e1 and e2
         return result.set(e1y * e2z - e2y * e1z,
                           e1z * e2x - e2z * e1x,
                           e1x * e2y - e2x * e1y);

ferox-physics/src/test/java/com/ferox/physics/CollisionTest.java

-package com.ferox.physics;
-
-import com.ferox.input.KeyEvent.KeyCode;
-import com.ferox.input.logic.InputManager;
-import com.ferox.input.logic.InputState;
-import com.ferox.input.logic.KeyHeldCondition;
-import com.ferox.input.logic.KeyPressedCondition;
-import com.ferox.input.logic.Trigger;
-import com.ferox.math.AxisAlignedBox;
-import com.ferox.math.ColorRGB;
-import com.ferox.math.Const;
-import com.ferox.math.Matrix3;
-import com.ferox.math.Matrix4;
-import com.ferox.math.Quat4;
-import com.ferox.math.Vector3;
-import com.ferox.math.Vector4;
-import com.ferox.math.bounds.QuadTree;
-import com.ferox.physics.collision.ClosestPair;
-import com.ferox.physics.collision.CollisionAlgorithm;
-import com.ferox.physics.collision.CollisionBody;
-import com.ferox.physics.collision.Shape;
-import com.ferox.physics.collision.algorithm.GjkEpaCollisionAlgorithm;
-import com.ferox.physics.collision.shape.ConvexShape;
-import com.ferox.renderer.Framework;
-import com.ferox.renderer.OnscreenSurface;
-import com.ferox.renderer.OnscreenSurfaceOptions;
-import com.ferox.renderer.OnscreenSurfaceOptions.MultiSampling;
-import com.ferox.renderer.Renderer.PolygonType;
-import com.ferox.renderer.impl.lwjgl.LwjglFramework;
-import com.ferox.resource.BufferData;
-import com.ferox.resource.VertexAttribute;
-import com.ferox.resource.VertexBufferObject;
-import com.ferox.resource.VertexBufferObject.StorageMode;
-import com.ferox.scene.AmbientLight;
-import com.ferox.scene.Camera;
-import com.ferox.scene.DiffuseColor;
-import com.ferox.scene.DirectionLight;
-import com.ferox.scene.PointLight;
-import com.ferox.scene.Renderable;
-import com.ferox.scene.Transform;
-import com.ferox.scene.controller.CameraController;
-import com.ferox.scene.controller.SpatialIndexController;
-import com.ferox.scene.controller.VisibilityController;
-import com.ferox.scene.controller.WorldBoundsController;
-import com.ferox.scene.controller.ffp.FixedFunctionRenderController;
-import com.ferox.scene.controller.light.LightGroupController;
-import com.ferox.util.geom.Box;
-import com.ferox.util.geom.Geometry;
-import com.ferox.util.geom.Sphere;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.Entity;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.SimpleController;
-
-public class CollisionTest {
-    public static final double BOUNDS = 50;
-    public static final double MARGIN = 0.05;
-    
-    public static final double ANGLE_RATE = Math.PI / 4;
-    public static final double TRANSLATE_RATE = 4;
-    
-    public static final boolean A_SPHERE = false;
-    public static final boolean B_SPHERE = false;
-    
-    public static final double A_SIZE = 2;
-//    public static final double B_SIZE = 100;
-    public static final double B_SIZE = 2;
-    
-    public static void main(String[] args) throws Exception {
-        final Framework framework = LwjglFramework.create();
-        System.out.println("OpenGL: " + framework.getCapabilities().getVendor() + " " + framework.getCapabilities().getVersion());
-        
-        final EntitySystem system = new EntitySystem();
-        
-        AxisAlignedBox worldBounds = new AxisAlignedBox(new Vector3(-2 * BOUNDS - 1, -2 * BOUNDS - 1, -2 * BOUNDS - 1), 
-                                                        new Vector3(2 * BOUNDS + 1, 2 * BOUNDS + 1, 2 * BOUNDS + 1));
-        
-        // physics handling
-        system.getControllerManager().addController(new TransformController());
-        
-        // rendering
-        system.getControllerManager().addController(new WorldBoundsController());
-        system.getControllerManager().addController(new CameraController());
-        system.getControllerManager().addController(new SpatialIndexController(new QuadTree<Entity>(worldBounds, 6)));
-        system.getControllerManager().addController(new VisibilityController());
-        system.getControllerManager().addController(new LightGroupController(worldBounds));
-        system.getControllerManager().addController(new FixedFunctionRenderController(framework));
-        
-        OnscreenSurface surface = buildSurface(framework, system);
-        InputManager io = new InputManager(surface);
-
-        // build scene
-        
-        // ambient light
-        system.addEntity().add(AmbientLight.ID).getData().setColor(new ColorRGB(0.2, 0.2, 0.2));
-        
-        // a point light
-        Entity point = system.addEntity();
-        point.add(PointLight.ID).getData().setColor(new ColorRGB(0.5, 0.5, 0.5));
-        point.add(Transform.ID).getData().setMatrix(new Matrix4(1, 0, 0, BOUNDS / 2,
-                                                                0, 1, 0, BOUNDS / 2,
-                                                                0, 0, 1, BOUNDS / 2,
-                                                                0, 0, 0, 1));
-        
-        // a directed light, which casts shadows
-        Entity inf = system.addEntity();
-        inf.add(DirectionLight.ID).getData().setColor(new ColorRGB(1, 1, 1));
-        inf.add(Transform.ID);
-        
-        // shapes
-        Geometry aGeom = (A_SPHERE ? new Sphere(A_SIZE / 2 + MARGIN, 8) : new Box(A_SIZE + 2 * MARGIN));
-        Shape aPhys = (A_SPHERE ? new com.ferox.physics.collision.shape.Sphere(A_SIZE / 2) : new com.ferox.physics.collision.shape.Box(A_SIZE, A_SIZE, A_SIZE));
-        aPhys.setMargin(MARGIN);
-        Geometry bGeom = (B_SPHERE ? new Sphere(B_SIZE / 2 + MARGIN, 8) : new Box(B_SIZE + 2 * MARGIN));
-        Shape bPhys = (B_SPHERE ? new com.ferox.physics.collision.shape.Sphere(B_SIZE / 2) : new com.ferox.physics.collision.shape.Box(B_SIZE, B_SIZE, B_SIZE));
-        bPhys.setMargin(MARGIN);
-        
-        Entity a = system.addEntity();
-        a.add(Renderable.ID).getData().setVertices(aGeom.getVertices())
-                                      .setLocalBounds(aGeom.getBounds())
-                                      .setIndices(aGeom.getPolygonType(), aGeom.getIndices(), aGeom.getIndexOffset(), aGeom.getIndexCount());
-        a.add(DiffuseColor.ID).getData().setColor(new ColorRGB(1, .5, .5));
-        a.add(Transform.ID);
-                    
-        a.add(CollisionBody.ID).getData().setShape(aPhys)
-                                         .setTransform(new Matrix4(1, 0, 0, 0,
-                                                                   0, 1, 0, 5,
-                                                                   0, 0, 1, 0,
-                                                                   0, 0, 0, 1));
-//                                         .setTransform(new Matrix4(0.3014718305476797, 0.10220009472983765, 0.9479820019512202, 7.748553848709842,
-//                                                                   0.03161100381232058, 0.9926210063725548, -0.11706529009879056, 1.1190060017643766,
-//                                                                   -0.9529509325375618, 0.06525854997736887, 0.29601662424708963, 5.405042647953112,
-//                                                                   0.0, 0.0, 0.0, 1.0));
-
-        Entity b = system.addEntity();
-        b.add(Renderable.ID).getData().setVertices(bGeom.getVertices())
-                                      .setLocalBounds(bGeom.getBounds())
-                                      .setIndices(bGeom.getPolygonType(), bGeom.getIndices(), bGeom.getIndexOffset(), bGeom.getIndexCount());
-        b.add(DiffuseColor.ID).getData().setColor(new ColorRGB(.5, 1, .5));
-        b.add(Transform.ID);
-
-        b.add(CollisionBody.ID).getData().setShape(bPhys)
-                                         .setTransform(new Matrix4(1, 0, 0, 0,
-                                                                   0, 1, 0, -5,
-                                                                   0, 0, 1, 0,
-                                                                   0, 0, 0, 1));
-//                                         .setTransform(new Matrix4(1, 0, 0, 0,
-//                                                                   0, 1, 0, -50,
-//                                                                   0, 0, 1, 0,
-//                                                                   0, 0, 0, 1));
-        
-        Entity c = system.addEntity();
-        c.add(Renderable.ID).getData().setVertices(new VertexAttribute(new VertexBufferObject(new BufferData(new float[6]), StorageMode.IN_MEMORY), 3))
-                                      .setArrayIndices(PolygonType.LINES, 0, 2);
-        c.add(DiffuseColor.ID);
-        
-        final Setup setup = new Setup(a, b, c);
-        
-        // configure input handling
-        
-        // rotate about x axis
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double angle = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * ANGLE_RATE;
-                Quat4 rotation = new Quat4().setAxisAngle(new Vector3(1, 0, 0), angle);
-                Matrix4 t = new Matrix4().setIdentity().setUpper(new Matrix3().set(rotation));
-                setup.transformActive(t, false);
-            }
-        }, new KeyHeldCondition(KeyCode.W));
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double angle = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * ANGLE_RATE;
-                Quat4 rotation = new Quat4().setAxisAngle(new Vector3(1, 0, 0), -angle);
-                Matrix4 t = new Matrix4().setIdentity().setUpper(new Matrix3().set(rotation));
-                setup.transformActive(t, false);
-            }
-        }, new KeyHeldCondition(KeyCode.S));
-        
-        // rotate about y axis
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double angle = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * ANGLE_RATE;
-                Quat4 rotation = new Quat4().setAxisAngle(new Vector3(0, 1, 0), angle);
-                Matrix4 t = new Matrix4().setIdentity().setUpper(new Matrix3().set(rotation));
-                setup.transformActive(t, false);
-            }
-        }, new KeyHeldCondition(KeyCode.A));
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double angle = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * ANGLE_RATE;
-                Quat4 rotation = new Quat4().setAxisAngle(new Vector3(0, 1, 0), -angle);
-                Matrix4 t = new Matrix4().setIdentity().setUpper(new Matrix3().set(rotation));
-                setup.transformActive(t, false);
-            }
-        }, new KeyHeldCondition(KeyCode.D));
-        
-        // rotate about z
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double angle = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * ANGLE_RATE;
-                Quat4 rotation = new Quat4().setAxisAngle(new Vector3(0, 0, 1), angle);
-                Matrix4 t = new Matrix4().setIdentity().setUpper(new Matrix3().set(rotation));
-                setup.transformActive(t, false);
-            }
-        }, new KeyHeldCondition(KeyCode.E));
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double angle = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * ANGLE_RATE;
-                Quat4 rotation = new Quat4().setAxisAngle(new Vector3(0, 0, 1), -angle);
-                Matrix4 t = new Matrix4().setIdentity().setUpper(new Matrix3().set(rotation));
-                setup.transformActive(t, false);
-            }
-        }, new KeyHeldCondition(KeyCode.Q));
-        
-        // translate x axis
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double delta = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * TRANSLATE_RATE;
-                Matrix4 t = new Matrix4().setIdentity();
-                t.setCol(3, new Vector4(delta, 0, 0, 1));
-                setup.transformActive(t, true);
-            }
-        }, new KeyHeldCondition(KeyCode.R));
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double delta = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * TRANSLATE_RATE;
-                Matrix4 t = new Matrix4().setIdentity();
-                t.setCol(3, new Vector4(-delta, 0, 0, 1));
-                setup.transformActive(t, true);
-            }
-        }, new KeyHeldCondition(KeyCode.T));
-        
-        // translate y axis
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double delta = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * TRANSLATE_RATE;
-                Matrix4 t = new Matrix4().setIdentity();
-                t.setCol(3, new Vector4(0, delta, 0, 1));
-                setup.transformActive(t, true);
-            }
-        }, new KeyHeldCondition(KeyCode.F));
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double delta = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * TRANSLATE_RATE;
-                Matrix4 t = new Matrix4().setIdentity();
-                t.setCol(3, new Vector4(0, -delta, 0, 1));
-                setup.transformActive(t, true);
-            }
-        }, new KeyHeldCondition(KeyCode.G));
-        
-        // translate z axis
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double delta = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * TRANSLATE_RATE;
-                Matrix4 t = new Matrix4().setIdentity();
-                t.setCol(3, new Vector4(0, 0, delta, 1));
-                setup.transformActive(t, true);
-            }
-        }, new KeyHeldCondition(KeyCode.V));
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                double delta = (next.getTimestamp() - prev.getTimestamp()) / 1000.0 * TRANSLATE_RATE;
-                Matrix4 t = new Matrix4().setIdentity();
-                t.setCol(3, new Vector4(0, 0, -delta, 1));
-                setup.transformActive(t, true);
-            }
-        }, new KeyHeldCondition(KeyCode.B));
-        
-        // toggle active
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                setup.toggleActive();
-            }
-        }, new KeyPressedCondition(KeyCode.SPACE));
-        
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                framework.destroy();
-            }
-        }, new KeyPressedCondition(KeyCode.ESCAPE));
-        
-        // main loop
-        setup.updateCollision();
-        while(!framework.isDestroyed()) {
-            io.process();
-            system.getControllerManager().process(1 / 60.0);
-            try {
-                Thread.sleep(10);
-            } catch(InterruptedException e) {
-                
-            }
-        }
-    }
-    
-    private static OnscreenSurface buildSurface(Framework framework, EntitySystem system) {
-        OnscreenSurfaceOptions options = new OnscreenSurfaceOptions().setWidth(500)
-                                                                     .setHeight(500)
-                                                                     .setMultiSampling(MultiSampling.FOUR_X);
-        OnscreenSurface surface = framework.createSurface(options);
-        surface.setVSyncEnabled(true);
-
-        // camera
-        Entity camera = system.addEntity();
-        camera.add(Camera.ID).getData().setSurface(surface)
-                                       .setZDistances(1.0, 6 * BOUNDS);
-        camera.add(Transform.ID).getData().setMatrix(new Matrix4(1, 0, 0, 0, 
-                                                                 0, 1, 0, 0,
-                                                                 0, 0, -1,  1.5 * BOUNDS,
-                                                                 0, 0, 0, 1));
-        return surface;
-    }
-    
-    private static class TransformController extends SimpleController {
-        @Override
-        public void process(double dt) {
-            CollisionBody cb = getEntitySystem().createDataInstance(CollisionBody.ID);
-            Transform t = getEntitySystem().createDataInstance(Transform.ID);
-            
-            ComponentIterator it = new ComponentIterator(getEntitySystem());
-            it.addRequired(cb);
-            it.addRequired(t);
-            
-            while(it.next()) {
-                t.setMatrix(cb.getTransform());
-            }
-        }
-    }
-    
-    private static class Setup {
-        private final Entity a;
-        private final Entity b;
-        
-        private final Entity collision;
-        
-        private Entity active;
-        private CollisionAlgorithm<ConvexShape, ConvexShape> algo;
-        
-        public Setup(Entity a, Entity b, Entity collision) {
-            this.a = a;
-            this.b = b;
-            this.collision = collision;
-            
-            active = a;
-            algo = new GjkEpaCollisionAlgorithm();
-            collision.get(Renderable.ID, true).setEnabled(false);
-        }
-        
-        public void transformActive(@Const Matrix4 transform, boolean inFront) {
-            CollisionBody active = this.active.get(CollisionBody.ID).getData();
-            if (inFront)
-                active.setTransform(active.getTransform().mul(transform, active.getTransform()));
-            else
-                active.setTransform(active.getTransform().mul(active.getTransform(), transform));
-            updateCollision();
-        }
-        
-        public void updateCollision() {
-            CollisionBody a = this.a.get(CollisionBody.ID).getData();
-            CollisionBody b = this.b.get(CollisionBody.ID).getData();
-            
-            ClosestPair pair = algo.getClosestPair((ConvexShape) a.getShape(), a.getTransform(), (ConvexShape) b.getShape(), b.getTransform());
-            
-            if (pair == null) {
-                // disable rendering
-                collision.get(Renderable.ID, true).setEnabled(false);
-                System.err.println("No collision");
-            } else {
-                // enable, and update geometry
-                Renderable r = collision.get(Renderable.ID, true).getData();
-                r.setEnabled(true);
-                
-                float[] vs = r.getVertices().getData().getData().getArray();
-                pair.getClosestPointOnA().get(vs, 0);
-                pair.getClosestPointOnB().get(vs, 3);
-                r.getVertices().getData().markDirty(0, 6);
-                
-                r.setLocalBounds(new AxisAlignedBox(vs, 0, 0, 2));
-                
-                if (pair.isIntersecting()) {
-                    // line is red
-                    collision.get(DiffuseColor.ID).getData().setColor(new ColorRGB(1, 0, 0));
-                } else {
-                    // line is green
-                    collision.get(DiffuseColor.ID).getData().setColor(new ColorRGB(0, 1, 0));
-                }
-                System.out.println(pair);
-            }
-        }
-        
-        public void toggleActive() {
-            if (active == a)
-                active = b;
-            else
-                active = a;
-        }
-    }
-}

ferox-physics/src/test/java/com/ferox/physics/GravityTest.java

 package com.ferox.physics;
 
-import com.ferox.input.KeyEvent.KeyCode;
-import com.ferox.input.logic.InputManager;
-import com.ferox.input.logic.InputState;
-import com.ferox.input.logic.KeyPressedCondition;
-import com.ferox.input.logic.Trigger;
-import com.ferox.math.AxisAlignedBox;
 import com.ferox.math.ColorRGB;
 import com.ferox.math.Matrix4;
 import com.ferox.math.Vector3;
-import com.ferox.math.bounds.QuadTree;
 import com.ferox.physics.collision.CollisionBody;
-import com.ferox.physics.collision.DefaultCollisionAlgorithmProvider;
-import com.ferox.physics.controller.ConstraintSolvingController;
-import com.ferox.physics.controller.ForcesController;
-import com.ferox.physics.controller.MotionController;
-import com.ferox.physics.controller.SpatialIndexCollisionController;
 import com.ferox.physics.dynamics.Gravity;
 import com.ferox.physics.dynamics.RigidBody;
-import com.ferox.renderer.Framework;
 import com.ferox.renderer.OnscreenSurface;
-import com.ferox.renderer.OnscreenSurfaceOptions;
-import com.ferox.renderer.OnscreenSurfaceOptions.MultiSampling;
-import com.ferox.renderer.impl.lwjgl.LwjglFramework;
 import com.ferox.resource.VertexBufferObject.StorageMode;
 import com.ferox.scene.AmbientLight;
 import com.ferox.scene.BlinnPhongMaterial;
 import com.ferox.scene.PointLight;
 import com.ferox.scene.Renderable;
 import com.ferox.scene.Transform;
-import com.ferox.scene.controller.CameraController;
-import com.ferox.scene.controller.SpatialIndexController;
-import com.ferox.scene.controller.VisibilityController;
-import com.ferox.scene.controller.WorldBoundsController;
-import com.ferox.scene.controller.ffp.FixedFunctionRenderController;
-import com.ferox.scene.controller.light.LightGroupController;
 import com.ferox.util.geom.Geometry;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.Controller;
 import com.lhkbob.entreri.Entity;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.SimpleController;
 
-public class GravityTest {
+public class GravityTest extends PhysicsApplicationStub {
     private static final StorageMode COMPILE_TYPE = StorageMode.GPU_STATIC;
-    private static final int BOUNDS = 20;
    
-    private static final double MARGIN = .05;
-    
-    private static final boolean STEP_ONLY = false;
-    
-    private static volatile boolean paused = true;
-    
-    public static void main(String[] args) throws Exception {
-        final Framework framework = LwjglFramework.create();
-        System.out.println("OpenGL: " + framework.getCapabilities().getVendor() + " " + framework.getCapabilities().getVersion());
+    @Override
+    protected void init(OnscreenSurface surface) {
+        super.init(surface);
         
-        final EntitySystem system = new EntitySystem();
-        
-        AxisAlignedBox worldBounds = new AxisAlignedBox(new Vector3(-2 * BOUNDS - 1, -2 * BOUNDS - 1, -2 * BOUNDS - 1), 
-                                                        new Vector3(2 * BOUNDS + 1, 2 * BOUNDS + 1, 2 * BOUNDS + 1));
-        
-        // physics handling
-        system.getControllerManager().addController(new ForcesController());
-//        system.getControllerManager().addController(new SpatialIndexCollisionController(new com.ferox.math.bounds.Octree<Entity>(worldBounds, 6), new DefaultCollisionAlgorithmProvider()));
-//        system.getControllerManager().addController(new SpatialIndexCollisionController(new com.ferox.math.bounds.QuadTree<Entity>(worldBounds, 6), new DefaultCollisionAlgorithmProvider()));
-        system.getControllerManager().addController(new SpatialIndexCollisionController(new com.ferox.math.bounds.SimpleSpatialIndex<Entity>(), new DefaultCollisionAlgorithmProvider()));
-
-        final MotionController m = new MotionController();
-        system.getControllerManager().addController(new ConstraintSolvingController());
-        system.getControllerManager().addController(m);
-        system.getControllerManager().addController(new TransformController());
-        
-        // rendering
-        system.getControllerManager().addController(new WorldBoundsController());
-        system.getControllerManager().addController(new CameraController());
-        system.getControllerManager().addController(new SpatialIndexController(new QuadTree<Entity>(worldBounds, 6)));
-        system.getControllerManager().addController(new VisibilityController());
-        system.getControllerManager().addController(new LightGroupController(worldBounds));
-        system.getControllerManager().addController(new FixedFunctionRenderController(framework));
-        
-        OnscreenSurface surface = buildSurface(framework, system);
-        buildScene(system);
-        
-        InputManager io = new InputManager(surface);
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                if (STEP_ONLY)
-                    process(system, 1, 0, true);
-                else
-                    paused = !paused;
-            }
-        }, new KeyPressedCondition(KeyCode.SPACE));
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                framework.destroy();
-            }
-        }, new KeyPressedCondition(KeyCode.ESCAPE));
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                System.out.println("break point");
-            }
-        }, new KeyPressedCondition(KeyCode.D));
-        
-        
-        long start = System.currentTimeMillis();
-        int  numFrames = 0;
-        while(!framework.isDestroyed()) {
-            io.process();
-            if (!paused && !STEP_ONLY) {
-                long now = System.currentTimeMillis();
-                process(system, numFrames, (now - start) / 1e3, (now - start) > 1000);
-                numFrames++;
-                if (now - start > 1000) {
-                    start = now;
-                    numFrames = 0;
-                }
-            } else {
-                start = System.currentTimeMillis();
-                numFrames = 0;
-                try {
-                    Thread.sleep(5);
-                } catch(InterruptedException e) { }
-            }
-        }
-    }
-    
-    private static void process(EntitySystem system, int numFrames, double time, boolean printStats) {
-        system.getControllerManager().process(1 / 60.0);
-        
-        if (printStats) {
-            System.out.println("Avg FPS: " + (numFrames / time));
-            
-            long total = 0;
-            for (Controller c: system.getControllerManager().getControllers()) {
-                total += system.getControllerManager().getExecutionTime(c);
-                System.out.printf(" - %s: %.3f ms\n", c.getClass().getSimpleName(), (system.getControllerManager().getExecutionTime(c) / 1e6));
-            }
-            System.out.printf(" - total: %.3f ms\n", total / 1e6);
-        }
-    }
-    
-    private static OnscreenSurface buildSurface(Framework framework, EntitySystem system) {
-        OnscreenSurfaceOptions options = new OnscreenSurfaceOptions().setWidth(500)
-                                                                     .setHeight(500)
-                                                                     .setMultiSampling(MultiSampling.NONE);
-        OnscreenSurface surface = framework.createSurface(options);
-        surface.setVSyncEnabled(true);
-        surface.setTitle(GravityTest.class.getSimpleName());
-
         // camera
         Entity camera = system.addEntity();
         camera.add(Camera.ID).getData().setSurface(surface)
                                                                  0, 1, 0, 0,
                                                                  0, 0, -1, .75 * BOUNDS,
                                                                  0, 0, 0, 1));
-        return surface;
-    }
-    
-    private static void buildScene(EntitySystem scene) throws Exception {
+        
         // shapes
-        Geometry geomShape1 = new com.ferox.util.geom.Box(2 + 2 * MARGIN, COMPILE_TYPE);
+        Geometry geomShape1 = com.ferox.util.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 = new com.ferox.util.geom.Box(2 + 2 * MARGIN, COMPILE_TYPE);
+        Geometry geomShape2 = com.ferox.util.geom.Box.create(2 + 2 * MARGIN, COMPILE_TYPE);
         com.ferox.physics.collision.Shape physShape2 = new com.ferox.physics.collision.shape.Box(2, 2, 2);
 
 //        Geometry geomShape1 = new com.ferox.util.geom.Sphere(1 + MARGIN, 16, COMPILE_TYPE);
         physShape2.setMargin(MARGIN);
         
         // falling down entity
-        Entity e = scene.addEntity();
+        Entity e = system.addEntity();
         e.add(Renderable.ID).getData().setVertices(geomShape1.getVertices())
                                       .setLocalBounds(geomShape1.getBounds())
                                       .setIndices(geomShape1.getPolygonType(), geomShape1.getIndices(), geomShape1.getIndexOffset(), geomShape1.getIndexCount());
         e.add(Gravity.ID).getData().setGravity(new Vector3(0, -10, 0));
         
         // falling up entity
-        e = scene.addEntity();
+        e = system.addEntity();
         e.add(Renderable.ID).getData().setVertices(geomShape2.getVertices())
                                       .setLocalBounds(geomShape2.getBounds())
                                       .setIndices(geomShape2.getPolygonType(), geomShape2.getIndices(), geomShape2.getIndexOffset(), geomShape2.getIndexCount());
         e.add(Gravity.ID).getData().setGravity(new Vector3(0, 10, 0));
         
         // ambient light
-        scene.addEntity().add(AmbientLight.ID).getData().setColor(new ColorRGB(0.2, 0.2, 0.2));
+        system.addEntity().add(AmbientLight.ID).getData().setColor(new ColorRGB(0.2, 0.2, 0.2));
         
         // a point light
-        Entity point = scene.addEntity();
+        Entity point = system.addEntity();
         point.add(PointLight.ID).getData().setColor(new ColorRGB(0.5, 0.5, 0.5));
         point.add(Transform.ID).getData().setMatrix(new Matrix4(1, 0, 0, BOUNDS / 2,
                                                                 0, 1, 0, BOUNDS / 2,
                                                                 0, 0, 0, 1));
     }
     
-    private static class TransformController extends SimpleController {
-        @Override
-        public void process(double dt) {
-            CollisionBody cb = getEntitySystem().createDataInstance(CollisionBody.ID);
-            Transform t = getEntitySystem().createDataInstance(Transform.ID);
-            
-            ComponentIterator it = new ComponentIterator(getEntitySystem());
-            it.addRequired(cb);
-            it.addRequired(t);
-            
-            while(it.next()) {
-                t.setMatrix(cb.getTransform());
-            }
-        }
+    public static void main(String[] args) throws Exception {
+        new GravityTest().run();
     }
 }

ferox-physics/src/test/java/com/ferox/physics/PhysicsApplicationStub.java

+package com.ferox.physics;
+
+import com.ferox.input.KeyEvent.KeyCode;
+import com.ferox.input.logic.InputManager;
+import com.ferox.input.logic.InputState;
+import com.ferox.input.logic.KeyPressedCondition;
+import com.ferox.input.logic.Trigger;
+import com.ferox.math.AxisAlignedBox;
+import com.ferox.math.Vector3;
+import com.ferox.math.bounds.QuadTree;
+import com.ferox.physics.collision.CollisionBody;
+import com.ferox.physics.collision.DefaultCollisionAlgorithmProvider;
+import com.ferox.physics.controller.SpatialIndexCollisionController;
+import com.ferox.renderer.OnscreenSurface;
+import com.ferox.renderer.impl.lwjgl.LwjglFramework;
+import com.ferox.scene.Transform;
+import com.ferox.scene.controller.CameraController;
+import com.ferox.scene.controller.SpatialIndexController;
+import com.ferox.scene.controller.VisibilityController;
+import com.ferox.scene.controller.WorldBoundsController;
+import com.ferox.scene.controller.ffp.FixedFunctionRenderController;
+import com.ferox.scene.controller.light.LightGroupController;
+import com.ferox.util.ApplicationStub;
+import com.lhkbob.entreri.ComponentIterator;
+import com.lhkbob.entreri.Entity;
+import com.lhkbob.entreri.EntitySystem;
+import com.lhkbob.entreri.SimpleController;
+
+public class PhysicsApplicationStub extends ApplicationStub {
+    protected static final int BOUNDS = 100;
+    protected static final double MARGIN = .05;
+    
+    protected static final AxisAlignedBox worldBounds = new AxisAlignedBox(new Vector3(-2 * BOUNDS - 1, -2 * BOUNDS - 1, -2 * BOUNDS - 1), 
+                                                                           new Vector3(2 * BOUNDS + 1, 2 * BOUNDS + 1, 2 * BOUNDS + 1));
+
+    private boolean paused;
+    private boolean stepOnce;
+    
+    protected final EntitySystem system;
+    
+    public PhysicsApplicationStub() {
+        super(LwjglFramework.create());
+        system = new EntitySystem();
+    }
+    
+    @Override
+    protected void installInputHandlers(InputManager io) {
+        io.addTrigger(new Trigger() {
+            @Override
+            public void onTrigger(InputState prev, InputState next) {
+                paused = !paused;
+            }
+        }, new KeyPressedCondition(KeyCode.SPACE));
+        io.addTrigger(new Trigger() {
+            @Override
+            public void onTrigger(InputState prev, InputState next) {
+                stepOnce = true;
+            }
+        }, new KeyPressedCondition(KeyCode.S));
+    }
+
+    @Override
+    protected void init(OnscreenSurface surface) {
+        // physics handling
+        system.getControllerManager().addController(new com.ferox.physics.controller.ForcesController());
+
+//        system.getControllerManager().addController(new SpatialIndexCollisionController(new com.ferox.math.bounds.QuadTree<Entity>(worldBounds, 6), new DefaultCollisionAlgorithmProvider()));
+//        system.getControllerManager().addController(new SpatialIndexCollisionController(new com.ferox.math.bounds.SimpleSpatialIndex<Entity>(), new DefaultCollisionAlgorithmProvider()));
+        system.getControllerManager().addController(new SpatialIndexCollisionController(new com.ferox.math.bounds.Octree<Entity>(worldBounds, 6), new DefaultCollisionAlgorithmProvider()));
+
+        system.getControllerManager().addController(new com.ferox.physics.controller.ConstraintSolvingController());
+        system.getControllerManager().addController(new com.ferox.physics.controller.MotionController());
+        
+        system.getControllerManager().addController(new TransformController());
+        
+        // rendering
+        system.getControllerManager().addController(new WorldBoundsController());
+        system.getControllerManager().addController(new CameraController());
+        system.getControllerManager().addController(new SpatialIndexController(new QuadTree<Entity>(worldBounds, 6)));
+        system.getControllerManager().addController(new VisibilityController());
+        system.getControllerManager().addController(new LightGroupController(worldBounds));
+        system.getControllerManager().addController(new FixedFunctionRenderController(surface.getFramework()));
+    }
+
+    @Override
+    protected void renderFrame(OnscreenSurface surface) {
+        if (!paused || stepOnce) {
+            system.getControllerManager().process(1 / 60.0);
+            stepOnce = false;
+        }
+    }
+
+    private static class TransformController extends SimpleController {
+        @Override
+        public void process(double dt) {
+            CollisionBody cb = getEntitySystem().createDataInstance(CollisionBody.ID);
+            Transform t = getEntitySystem().createDataInstance(Transform.ID);
+            
+            ComponentIterator it = new ComponentIterator(getEntitySystem());
+            it.addRequired(cb);
+            it.addRequired(t);
+            
+            while(it.next()) {
+                t.setMatrix(cb.getTransform());
+            }
+        }
+    }
+}

ferox-physics/src/test/java/com/ferox/physics/PhysicsTest.java

 package com.ferox.physics;
 
-import com.ferox.input.KeyEvent.KeyCode;
-import com.ferox.input.logic.InputManager;
-import com.ferox.input.logic.InputState;
-import com.ferox.input.logic.KeyPressedCondition;
-import com.ferox.input.logic.Trigger;
-import com.ferox.math.AxisAlignedBox;
 import com.ferox.math.ColorRGB;
 import com.ferox.math.Matrix4;
-import com.ferox.math.Vector3;
-import com.ferox.math.bounds.QuadTree;
 import com.ferox.physics.collision.CollisionBody;
-import com.ferox.physics.collision.DefaultCollisionAlgorithmProvider;
-import com.ferox.physics.controller.SpatialIndexCollisionController;
 import com.ferox.physics.dynamics.RigidBody;
-import com.ferox.renderer.Framework;
 import com.ferox.renderer.OnscreenSurface;
-import com.ferox.renderer.OnscreenSurfaceOptions;
-import com.ferox.renderer.OnscreenSurfaceOptions.MultiSampling;
-import com.ferox.renderer.impl.lwjgl.LwjglFramework;
 import com.ferox.resource.VertexBufferObject.StorageMode;
 import com.ferox.scene.AmbientLight;
 import com.ferox.scene.BlinnPhongMaterial;
 import com.ferox.scene.PointLight;
 import com.ferox.scene.Renderable;
 import com.ferox.scene.Transform;
-import com.ferox.scene.controller.CameraController;
-import com.ferox.scene.controller.SpatialIndexController;
-import com.ferox.scene.controller.VisibilityController;
-import com.ferox.scene.controller.WorldBoundsController;
-import com.ferox.scene.controller.ffp.FixedFunctionRenderController;
-import com.ferox.scene.controller.light.LightGroupController;
 import com.ferox.util.geom.Box;
 import com.ferox.util.geom.Geometry;
-import com.ferox.util.geom.Sphere;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.Controller;
 import com.lhkbob.entreri.Entity;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.SimpleController;
 
-public class PhysicsTest {
+public class PhysicsTest extends PhysicsApplicationStub {
     private static final StorageMode COMPILE_TYPE = StorageMode.GPU_STATIC;
-    private static final int BOUNDS = 100;
    
-    private static final int NUM_X = 10;
-    private static final int NUM_Y = 10;
-    private static final int NUM_Z = 10;
+    private static final int NUM_X = 5;
+    private static final int NUM_Y = 5;
+    private static final int NUM_Z = 5;
     private static final double SCALE_X = 3.0;
     private static final double SCALE_Y = 2.0;
     private static final double SCALE_Z = 3.0;
     
-    private static final double MARGIN = .05;
-    private static final double PERCENT = .5;
-
     private static final double RANDOM = 0;
     
     private static final double START_POS_X = -5;
     private static final double START_POS_Y = 1 + 2 * MARGIN;
     private static final double START_POS_Z = -3;
     
-    private static final AxisAlignedBox worldBounds = new AxisAlignedBox(new Vector3(-2 * BOUNDS - 1, -2 * BOUNDS - 1, -2 * BOUNDS - 1), 
-                                                                         new Vector3(2 * BOUNDS + 1, 2 * BOUNDS + 1, 2 * BOUNDS + 1));
-    
-    private static volatile boolean paused = false;
-    
-    public static void main(String[] args) throws Exception {
-        final Framework framework = LwjglFramework.create();
-        System.out.println("OpenGL: " + framework.getCapabilities().getVendor() + " " + framework.getCapabilities().getVersion());
-        
-        final EntitySystem system = new EntitySystem();
-        
-        // physics handling
-        system.getControllerManager().addController(new com.ferox.physics.controller.ForcesController());
-
-//        system.getControllerManager().addController(new SpatialIndexCollisionController(new com.ferox.math.bounds.QuadTree<Entity>(worldBounds, 6), new DefaultCollisionAlgorithmProvider()));
-//        system.getControllerManager().addController(new SpatialIndexCollisionController(new com.ferox.math.bounds.SimpleSpatialIndex<Entity>(), new DefaultCollisionAlgorithmProvider()));
-        system.getControllerManager().addController(new SpatialIndexCollisionController(new com.ferox.math.bounds.Octree<Entity>(worldBounds, 6), new DefaultCollisionAlgorithmProvider()));
-
-        system.getControllerManager().addController(new com.ferox.physics.controller.ConstraintSolvingController());
-        system.getControllerManager().addController(new com.ferox.physics.controller.MotionController());
-        
-        system.getControllerManager().addController(new TransformController());
-        
-        // rendering
-        system.getControllerManager().addController(new WorldBoundsController());
-        system.getControllerManager().addController(new CameraController());
-        system.getControllerManager().addController(new SpatialIndexController(new QuadTree<Entity>(worldBounds, 6)));
-        system.getControllerManager().addController(new VisibilityController());
-        system.getControllerManager().addController(new LightGroupController(worldBounds));
-        system.getControllerManager().addController(new FixedFunctionRenderController(framework));
-        
-        OnscreenSurface surface = buildSurface(framework, system);
-        buildScene(system);
-        
-        InputManager io = new InputManager(surface);
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                paused = !paused;
-            }
-        }, new KeyPressedCondition(KeyCode.SPACE));
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                process(system, 1, 0, false);
-            }
-        }, new KeyPressedCondition(KeyCode.S));
-        io.addTrigger(new Trigger() {
-            @Override
-            public void onTrigger(InputState prev, InputState next) {
-                framework.destroy();
-            }
-        }, new KeyPressedCondition(KeyCode.ESCAPE));
-        
-        long start = System.currentTimeMillis();
-        int  numFrames = 0;
-        while(!framework.isDestroyed()) {
-            io.process();
-            if (!paused) {
-                long now = System.currentTimeMillis();
-                process(system, numFrames, (now - start) / 1e3, (now - start) > 1000);
-                numFrames++;
-                if (now - start > 1000) {
-                    start = now;
-                    numFrames = 0;
-                }
-            } else {
-                start = System.currentTimeMillis();
-                numFrames = 0;
-                try {
-                    Thread.sleep(5);
-                } catch(InterruptedException e) { }
-            }
-        }
-    }
-    
-    private static void process(EntitySystem system, int numFrames, double time, boolean printStats) {
-        system.getControllerManager().process(1 / 60.0);
-        
-        if (printStats) {
-            System.out.println("Avg FPS: " + (numFrames / time));
-            
-            long total = 0;
-            for (Controller c: system.getControllerManager().getControllers()) {
-                total += system.getControllerManager().getExecutionTime(c);
-                System.out.printf(" - %s: %.3f ms\n", c.getClass().getSimpleName(), (system.getControllerManager().getExecutionTime(c) / 1e6));
-            }
-            System.out.printf(" - total: %.3f ms\n", total / 1e6);
-        }
-    }
-    
-    private static OnscreenSurface buildSurface(Framework framework, EntitySystem system) {
-        OnscreenSurfaceOptions options = new OnscreenSurfaceOptions().setWidth(500)
-                                                                     .setHeight(500);
-//                                                                     .setMultiSampling(MultiSampling.FOUR_X);
-        OnscreenSurface surface = framework.createSurface(options);
-//        surface.setVSyncEnabled(true);
+    @Override
+    protected void init(OnscreenSurface surface) {
+        super.init(surface);
 
         // camera
         Entity camera = system.addEntity();
                                                                  0, .577, 0, .2 * BOUNDS,
                                                                  .707, -.577, -.707,  .3 * BOUNDS,
                                                                  0, 0, 0, 1));
-        return surface;
-    }
-    
-    private static void buildScene(EntitySystem scene) throws Exception {
+        
         // shapes
-        Geometry box = new Box(2 + 2 * MARGIN, COMPILE_TYPE);
-        Geometry sphere = new Sphere(1 + MARGIN, 8, COMPILE_TYPE);
+        Geometry box = Box.create(2 + 2 * MARGIN, COMPILE_TYPE);
+//        Geometry sphere = Sphere.create(1 + MARGIN, 8, COMPILE_TYPE);
         
         com.ferox.physics.collision.Shape boxShape = new com.ferox.physics.collision.shape.Box(2, 2, 2);
-        com.ferox.physics.collision.Shape sphereShape = new com.ferox.physics.collision.shape.Sphere(1);
+//        com.ferox.physics.collision.Shape sphereShape = new com.ferox.physics.collision.shape.Sphere(1);
         boxShape.setMargin(MARGIN);
-        sphereShape.setMargin(MARGIN);
+//        sphereShape.setMargin(MARGIN);
         
         double startX = START_POS_X - NUM_X / 2.0;
         double startY = START_POS_Y;
                     Geometry geomShape;
                     ColorRGB color;
                     
-//                    if (Math.random() > PERCENT) {
-//                    if (z == 0 || z == NUM_Z - 1 || y == 0 || y == NUM_Y - 1 || x == 0 || x == NUM_X - 1) {
-                    if (true) {
-//                    if (false) {
-                        physShape = boxShape;
-                        geomShape = box;
-                        color = new ColorRGB(.7, .2, .2);
-                    } else {
-                        physShape = sphereShape;
-                        geomShape = sphere;
-                        color = new ColorRGB(0, 0, .8);
-                    }
+                    physShape = boxShape;
+                    geomShape = box;
+                    color = new ColorRGB(.7, .2, .2);
                     
                     double rx = (Math.random() * randXLim - randXLim / 2);
                     double ry = (Math.random() * randYLim - randYLim / 2);
                     double rz = (Math.random() * randZLim - randZLim / 2);
 
-                    Entity e = scene.addEntity();
+                    Entity e = system.addEntity();
                     e.add(Renderable.ID).getData().setVertices(geomShape.getVertices())
                                                   .setLocalBounds(geomShape.getBounds())
                                                   .setIndices(geomShape.getPolygonType(), geomShape.getIndices(), geomShape.getIndexOffset(), geomShape.getIndexCount());
         }
         
         // some walls
-        Geometry bottomWall = new Box(BOUNDS);
-        Entity wall = scene.addEntity();
+        Geometry bottomWall = Box.create(BOUNDS + 2 * MARGIN, COMPILE_TYPE);
+        Entity wall = system.addEntity();
         wall.add(Renderable.ID).getData().setVertices(bottomWall.getVertices())
                                          .setLocalBounds(bottomWall.getBounds())
                                          .setIndices(bottomWall.getPolygonType(), bottomWall.getIndices(), bottomWall.getIndexOffset(), bottomWall.getIndexCount());
                                                                       0, 0, 0, 1));
         
         // ambient light
-        scene.addEntity().add(AmbientLight.ID).getData().setColor(new ColorRGB(0.2, 0.2, 0.2));
+        system.addEntity().add(AmbientLight.ID).getData().setColor(new ColorRGB(0.2, 0.2, 0.2));
         
         // a point light
-        Entity point = scene.addEntity();
+        Entity point = system.addEntity();
         point.add(PointLight.ID).getData().setColor(new ColorRGB(0.5, 0.5, 0.5));
         point.add(Transform.ID).getData().setMatrix(new Matrix4(1, 0, 0, BOUNDS / 2,
                                                                 0, 1, 0, BOUNDS / 2,
                                                                 0, 0, 0, 1));
         
         // a directed light, which casts shadows
-        Entity inf = scene.addEntity();
+        Entity inf = system.addEntity();
         inf.add(DirectionLight.ID).getData().setColor(new ColorRGB(1, 1, 1));
         inf.add(Transform.ID);
     }
-    
-    private static class TransformController extends SimpleController {
-        @Override
-        public void process(double dt) {
-            CollisionBody cb = getEntitySystem().createDataInstance(CollisionBody.ID);
-            Transform t = getEntitySystem().createDataInstance(Transform.ID);
-            
-            ComponentIterator it = new ComponentIterator(getEntitySystem());
-            it.addRequired(cb);
-            it.addRequired(t);
-            
-            while(it.next()) {
-                t.setMatrix(cb.getTransform());
-//                System.out.println(cb.getEntity().getId() + " " + cb.getTransform());
-            }
-        }
+
+    public static void main(String[] args) {
+        new PhysicsTest().run();
     }
 }

ferox-renderer/ferox-renderer-jogl/pom.xml

             <artifactId>ferox-renderer-impl</artifactId>
             <version>${project.version}</version>
         </dependency>
+        
+        <dependency>
+            <groupId>com.lhkbob.ferox</groupId>
+            <artifactId>ferox-test</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>

ferox-renderer/ferox-renderer-jogl/src/test/java/com/ferox/renderer/impl/jogl/FixedFunctionRenderTest.java

 package com.ferox.renderer.impl.jogl;
 
+import com.ferox.input.logic.InputManager;
 import com.ferox.math.Matrix4;
 import com.ferox.math.Vector3;
 import com.ferox.math.Vector4;
 import com.ferox.math.bounds.Frustum;
 import com.ferox.renderer.Context;
-import com.ferox.renderer.DisplayMode;
-import com.ferox.renderer.DisplayMode.PixelFormat;
 import com.ferox.renderer.FixedFunctionRenderer;
 import com.ferox.renderer.FixedFunctionRenderer.TexCoord;
 import com.ferox.renderer.FixedFunctionRenderer.TexCoordSource;
-import com.ferox.renderer.Framework;
 import com.ferox.renderer.HardwareAccessLayer;
 import com.ferox.renderer.OnscreenSurface;
-import com.ferox.renderer.OnscreenSurfaceOptions;
-import com.ferox.renderer.OnscreenSurfaceOptions.DepthFormat;
 import com.ferox.renderer.Renderer.DrawStyle;
 import com.ferox.renderer.Surface;
 import com.ferox.renderer.Task;
 import com.ferox.resource.Texture.Target;
 import com.ferox.resource.TextureFormat;
 import com.ferox.resource.VertexBufferObject.StorageMode;
+import com.ferox.util.ApplicationStub;
 import com.ferox.util.geom.Geometry;
 import com.ferox.util.geom.Sphere;
 
-public class FixedFunctionRenderTest {
+public class FixedFunctionRenderTest extends ApplicationStub {
+    private FixedFunctionPass pass;
     
-    public static void main(String[] args) throws Exception {
-        Framework framework = JoglFramework.create(false, false, true, false);
-        System.out.println(framework.getCapabilities().getGlslVersion() + " " + framework.getCapabilities().getMaxTexture3DSize());
-        OnscreenSurface window = framework.createSurface(new OnscreenSurfaceOptions().setWidth(800)
-                                                                                     .setHeight(600)
-                                                                                     .setFullscreenMode(new DisplayMode(1440, 900, PixelFormat.RGB_24BIT))
-                                                                                     .setResizable(false)
-                                                                                     .setDepthFormat(DepthFormat.DEPTH_24BIT));
-//        window.setVSyncEnabled(true);
-        FixedFunctionPass pass = new FixedFunctionPass(window);
-        try {
-            long now = System.currentTimeMillis();
-            int frames = 0;
-            while(true) {
-                if (window.isDestroyed())
-                    break;
-                framework.queue(pass).get();
-                framework.flush(window);
-                framework.sync();
-                
-                frames++;
-                if (System.currentTimeMillis() - now > 1000) {
-                    System.out.println("FPS: " + frames);
-                    frames = 0;
-                    now = System.currentTimeMillis();
-                }
-            }
-        } finally {
-            framework.destroy();
-        }
+    public FixedFunctionRenderTest() {
+        super(JoglFramework.create(false, false, true, false));
+    }
+    
+    @Override
+    protected void installInputHandlers(InputManager io) { }
+
+    @Override
+    protected void init(OnscreenSurface surface) {
+        pass = new FixedFunctionPass(surface);
+    }
+
+    @Override
+    protected void renderFrame(OnscreenSurface surface) {
+        surface.getFramework().queue(pass);
     }
     
     private static class FixedFunctionPass implements Task<Void> {
         public FixedFunctionPass(Surface surface) {
             this.surface = surface;
             
-            shape = new Sphere(2f, 32, StorageMode.GPU_STATIC);
+            shape = Sphere.create(2f, 32, StorageMode.GPU_STATIC);
             
             f = new Frustum(60f, surface.getWidth() / (float) surface.getHeight(), 1f, 100f);
             f.setOrientation(new Vector3(0f, 3f, 10f), new Vector3(0f, 0f, -1f), new Vector3(0f, 1f, 0f));
             return null;
         }
     }
+    
+    public static void main(String[] args){
+        new FixedFunctionRenderTest().run();
+    }
 }

ferox-renderer/ferox-renderer-jogl/src/test/java/com/ferox/renderer/impl/jogl/GlslRenderTest.java

 import java.util.Map;
 import java.util.Map.Entry;
 
+import com.ferox.input.logic.InputManager;
 import com.ferox.math.Vector3;
 import com.ferox.math.Vector4;
 import com.ferox.math.bounds.Frustum;
 import com.ferox.renderer.Context;
-import com.ferox.renderer.DisplayMode;
-import com.ferox.renderer.DisplayMode.PixelFormat;
 import com.ferox.renderer.Framework;
 import com.ferox.renderer.GlslRenderer;
 import com.ferox.renderer.HardwareAccessLayer;
 import com.ferox.renderer.OnscreenSurface;
-import com.ferox.renderer.OnscreenSurfaceOptions;
-import com.ferox.renderer.OnscreenSurfaceOptions.DepthFormat;
 import com.ferox.renderer.Surface;
 import com.ferox.renderer.Task;
 import com.ferox.resource.BufferData;
 import com.ferox.resource.Texture.Filter;
 import com.ferox.resource.Texture.Target;
 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 {
+public class GlslRenderTest extends ApplicationStub {
     private static final String VERTEX_SHADER = 
         "uniform mat4 projection;" + 
         "uniform mat4 modelview;" + 
         "   gl_FragColor = texture3D(texture, tcs) * color;" +
         "}";
     
-    public static void main(String[] args) throws Exception {
-        Framework framework = JoglFramework.create(false, false, true, false);
-        System.out.println(framework.getCapabilities().getGlslVersion() + " " + framework.getCapabilities().getMaxTexture3DSize());
-        OnscreenSurface window = framework.createSurface(new OnscreenSurfaceOptions().setWidth(800)
-                                                                                     .setHeight(600)
-                                                                                     .setFullscreenMode(new DisplayMode(1440, 900, PixelFormat.RGB_24BIT))
-                                                                                     .setResizable(false)
-                                                                                     .setDepthFormat(DepthFormat.DEPTH_24BIT));
-        
-        GlslPass pass = new GlslPass(window);
+    private GlslPass pass;
+    
+    public GlslRenderTest() {
+        super(JoglFramework.create(false, false, true, false));
+    }
+    
+    @Override
+    protected void installInputHandlers(InputManager io) { }
+
+    @Override
+    protected void init(OnscreenSurface surface) {
+        Framework framework = surface.getFramework();
+        pass = new GlslPass(surface);
         Status status = framework.update(pass.shader);
         if (status != Status.READY) {
             System.out.println("Shader: " + status + " " + framework.getStatusMessage(pass.shader));
             framework.destroy();
             System.exit(0);
         }
-        
-        try {
-            long now = System.currentTimeMillis();
-            int frames = 0;
-            while(true) {
-                if (window.isDestroyed())
-                    break;
-                framework.queue(pass).get();
-                framework.flush(window);
-                framework.sync();
-                
-                frames++;
-                if (System.currentTimeMillis() - now > 1000) {
-                    System.out.println("FPS: " + frames);
-                    frames = 0;
-                    now = System.currentTimeMillis();
-                }
-            }
-        } finally {
-            framework.destroy();
-        }
+    }
+
+    @Override
+    protected void renderFrame(OnscreenSurface surface) {
+        surface.getFramework().queue(pass);
+    }
+    
+    public static void main(String[] args) throws Exception {
+        new GlslRenderTest().run();
     }
     
     private static class GlslPass implements Task<Void> {
         public GlslPass(Surface surface) {
             this.surface = surface;
             
-            shape = new Box(4f);
+            shape = Box.create(4.0);
             shader = new GlslShader();
             
             shader.setShader(ShaderType.VERTEX, VERTEX_SHADER);

ferox-renderer/ferox-renderer-lwjgl/pom.xml

             <artifactId>lwjgl</artifactId>
             <version>2.8.4</version>
         </dependency>
+        
+        <dependency>
+            <groupId>com.lhkbob.ferox</groupId>
+            <artifactId>ferox-test</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>

ferox-renderer/ferox-renderer-lwjgl/src/test/java/com/ferox/renderer/impl/lwjgl/FixedFunctionRenderTest.java

-package com.ferox.renderer.impl.lwjgl;
-
-import com.ferox.math.Matrix4;
-import com.ferox.math.Vector3;
-import com.ferox.math.Vector4;
-import com.ferox.math.bounds.Frustum;
-import com.ferox.renderer.Context;
-import com.ferox.renderer.FixedFunctionRenderer;
-import com.ferox.renderer.FixedFunctionRenderer.TexCoord;
-import com.ferox.renderer.FixedFunctionRenderer.TexCoordSource;
-import com.ferox.renderer.Framework;
-import com.ferox.renderer.HardwareAccessLayer;
-import com.ferox.renderer.OnscreenSurface;
-import com.ferox.renderer.OnscreenSurfaceOptions;
-import com.ferox.renderer.OnscreenSurfaceOptions.DepthFormat;
-import com.ferox.renderer.Renderer.DrawStyle;
-import com.ferox.renderer.Surface;
-import com.ferox.renderer.Task;
-import com.ferox.resource.BufferData;
-import com.ferox.resource.Mipmap;
-import com.ferox.resource.Texture;
-import com.ferox.resource.Texture.Filter;
-import com.ferox.resource.Texture.Target;
-import com.ferox.resource.TextureFormat;
-import com.ferox.resource.VertexBufferObject.StorageMode;
-import com.ferox.util.geom.Box;
-import com.ferox.util.geom.Geometry;
-import com.ferox.util.geom.Sphere;
-import com.ferox.util.geom.Teapot;
-
-public class FixedFunctionRenderTest {
-    
-    public static void main(String[] args) throws Exception {
-        Framework framework = LwjglFramework.create();
-        System.out.println(framework.getCapabilities().getGlslVersion() + " " + framework.getCapabilities().getMaxTexture3DSize());
-        OnscreenSurface window = framework.createSurface(new OnscreenSurfaceOptions().setWidth(800)
-                                                                                     .setHeight(600)
-//                                                                                     .setFullscreenMode(new DisplayMode(1440, 900, PixelFormat.RGB_24BIT))
-                                                                                     .setResizable(false)
-                                                                                     .setDepthFormat(DepthFormat.DEPTH_24BIT));
-        window.setVSyncEnabled(true);
-        FixedFunctionPass pass = new FixedFunctionPass(window);
-        try {
-            long now = System.currentTimeMillis();
-            int frames = 0;
-            while(true) {
-                if (window.isDestroyed())
-                    break;
-                framework.queue(pass).get();
-                framework.flush(window);
-                framework.sync();
-                
-                frames++;
-                if (System.currentTimeMillis() - now > 1000) {
-                    Runtime r = Runtime.getRuntime();
-                    System.out.printf("Memory: %.2f of %.2f\n", (r.totalMemory() - r.freeMemory()) / (1024f * 1024f), r.totalMemory() / (1024f * 1024f));
-                    System.out.println("FPS: " + frames);
-                    frames = 0;
-                    now = System.currentTimeMillis();
-                }
-            }
-        } finally {
-            framework.destroy();
-        }
-    }
-    
-    private static class FixedFunctionPass implements Task<Void> {
-        final Geometry box;
-        final Geometry sphere;
-        final Geometry teapot;
-        
-        final Texture volume;
-        
-        final Frustum f;
-        
-        boolean statusChecked;
-        final Surface surface;
-        
-        public FixedFunctionPass(Surface surface) {
-            this.surface = surface;
-            
-            box = new Box(2f, StorageMode.GPU_STATIC);
-            sphere = new Sphere(2f, 32, StorageMode.GPU_STATIC);
-            teapot = new Teapot(.5f, StorageMode.GPU_STATIC);
-            
-            f = new Frustum(60f, surface.getWidth() / (float) surface.getHeight(), 1f, 100f);
-            f.setOrientation(new Vector3(0f, 3f, 10f), new Vector3(0f, 0f, -1f), new Vector3(0f, 1f, 0f));
-            
-            int width = 256;
-            int height = 256;
-            int depth = 256;
-            
-            byte[] volumeBuffer = new byte[width * height * depth * 4];
-            for (int z = 0; z < depth; z++) {
-                for (int y = 0; y < height; y++) {
-                    for (int x = 0; x < width; x++) {
-                        int index = z * width * height * 4 + y * width * 4 + x * 4;
-
-                        volumeBuffer[index] = (byte) (z / (float) depth * 256);
-                        volumeBuffer[index + 1] = (byte) (y / (float) height * 256);
-                        volumeBuffer[index + 2] = (byte) (x / (float) width * 256);
-                        volumeBuffer[index + 3] = (byte) 127;
-                    }
-                }
-            }
-
-            Mipmap data = new Mipmap(new BufferData(volumeBuffer), width, height, depth, TextureFormat.RGBA);
-            volume = new Texture(Target.T_3D, data);
-            volume.setFilter(Filter.NEAREST);
-        }
-        
-        @Override
-        public Void run(HardwareAccessLayer access) {
-            Context context = access.setActiveSurface(surface);
-            if (context == null)
-                return null;
-            
-            FixedFunctionRenderer g = context.getFixedFunctionRenderer();
-            if (g != null) {
-                g.clear(true, true, true, new Vector4(.2f, .2f, .2f, 1f), 1, 0);
-                
-                g.setDrawStyle(DrawStyle.SOLID);
-                
-                
-                
-                g.setTexture(0, volume);
-                g.setTextureCoordGeneration(0, TexCoord.R, TexCoordSource.OBJECT);
-                
-                g.setProjectionMatrix(f.getProjectionMatrix());
-                
-                Geometry shape;
-                Matrix4 t = new Matrix4();
-                int rendered = 0;
-                int num = 10000;
-                int thirds = num / 3;
-                for (int i = 0; i < num; i++) {
-                    t.setIdentity();
-                    t.set(0, 3, (float) Math.random() * 100 - 50);
-                    t.set(1, 3, (float) Math.random() * 100 - 50);
-                    t.set(2, 3, (float) Math.random() * 100 - 50);
-
-                    g.setModelViewMatrix(f.getViewMatrix().mul(t, t));
-
-//                    switch(i % 3) {
-                    switch(i / thirds) {
-//                    switch((int) (Math.random() * 3)) {
-                    case 0: shape = box; break;
-                    case 1: shape = sphere; break;
-                    case 2: shape = teapot; break;
-                    default: shape = sphere; break;
-                    }
-                    
-                    g.setVertices(shape.getVertices());
-                    g.setNormals(shape.getNormals());
-                    g.setTextureCoordinates(0, shape.getTextureCoordinates());
-                    
-                    if (shape.getIndices() != null)
-                        rendered += g.render(shape.getPolygonType(), shape.getIndices(), shape.getIndexOffset(), shape.getIndexCount());
-                    else
-                        rendered += g.render(shape.getPolygonType(), shape.getIndexOffset(), shape.getIndexCount());
-                }
-                
-                if (!statusChecked) {
-                    statusChecked = true;
-                    System.out.println("Rendered count: " + rendered);
-                }
-            }
-            
-            return null;
-        }
-    }
-}

ferox-renderer/ferox-renderer-lwjgl/src/test/java/com/ferox/renderer/impl/lwjgl/GlslRenderTest.java

-package com.ferox.renderer.impl.lwjgl;
-
-import java.util.Map;
-import java.util.Map.Entry;
-
-import com.ferox.math.Vector3;
-import com.ferox.math.Vector4;
-import com.ferox.math.bounds.Frustum;
-import com.ferox.renderer.Context;
-import com.ferox.renderer.DisplayMode;
-import com.ferox.renderer.DisplayMode.PixelFormat;
-import com.ferox.renderer.Framework;
-import com.ferox.renderer.GlslRenderer;
-import com.ferox.renderer.HardwareAccessLayer;
-import com.ferox.renderer.OnscreenSurface;
-import com.ferox.renderer.OnscreenSurfaceOptions;
-import com.ferox.renderer.OnscreenSurfaceOptions.DepthFormat;
-import com.ferox.renderer.Surface;
-import com.ferox.renderer.Task;
-import com.ferox.resource.BufferData;
-import com.ferox.resource.GlslShader;
-import com.ferox.resource.GlslShader.ShaderType;
-import com.ferox.resource.GlslUniform;
-import com.ferox.resource.Mipmap;
-import com.ferox.resource.Resource.Status;
-import com.ferox.resource.Texture;
-import com.ferox.resource.Texture.Filter;
-import com.ferox.resource.Texture.Target;
-import com.ferox.resource.TextureFormat;
-import com.ferox.util.geom.Box;
-import com.ferox.util.geom.Geometry;
-
-public class GlslRenderTest {
-    private static final String VERTEX_SHADER = 
-        "uniform mat4 projection;" + 
-        "uniform mat4 modelview;" + 
-        "uniform vec2 transform;" + 
-        
-        "attribute vec3 vertex;" + 
-        "varying vec3 tcs;" +
-        
-        "void main() {" +
-        "   tcs = vec3((vertex.x + transform.x) * transform.y, (vertex.y + transform.x) * transform.y, (vertex.z + transform.x) * transform.y);" +
-        "   gl_Position = projection * modelview * vec4(vertex, 1.0);" +
-        "}";
-    private static final String FRAGMENT_SHADER = 
-        "uniform vec4 color;" +
-        "uniform sampler3D texture;" +
-        
-        "varying vec3 tcs;" +
-        
-        "void main() {" +
-        "   gl_FragColor = texture3D(texture, tcs) * color;" +
-        "}";
-    
-    public static void main(String[] args) throws Exception {
-        Framework framework = LwjglFramework.create();
-        System.out.println(framework.getCapabilities().getGlslVersion() + " " + framework.getCapabilities().getMaxTexture3DSize());
-        OnscreenSurface window = framework.createSurface(new OnscreenSurfaceOptions().setWidth(800)
-                                                                                     .setHeight(600)
-                                                                                     .setFullscreenMode(new DisplayMode(1440, 900, PixelFormat.RGB_24BIT))
-                                                                                     .setResizable(false)
-                                                                                     .setDepthFormat(DepthFormat.DEPTH_24BIT));
-        
-        GlslPass pass = new GlslPass(window);
-        Status status = framework.update(pass.shader);
-        if (status != Status.READY) {
-            System.out.println("Shader: " + status + " " + framework.getStatusMessage(pass.shader));
-            framework.destroy();
-            System.exit(0);
-        }
-        
-        try {
-            long now = System.currentTimeMillis();
-            int frames = 0;
-            while(true) {
-                if (window.isDestroyed())
-                    break;
-                framework.queue(pass).get();
-                framework.flush(window);
-                framework.sync();
-                
-                frames++;
-                if (System.currentTimeMillis() - now > 1000) {
-                    Runtime r = Runtime.getRuntime();
-                    System.out.printf("Memory: %.2f of %.2f\n", (r.totalMemory() - r.freeMemory()) / (1024f * 1024f), r.totalMemory() / (1024f * 1024f));
-                    System.out.println("FPS: " + frames);
-                    frames = 0;
-                    now = System.currentTimeMillis();
-                }
-            }
-        } finally {
-            framework.destroy();
-        }
-    }
-    
-    private static class GlslPass implements Task<Void> {
-        final GlslShader shader;
-        final Geometry shape;
-        
-        final Texture volume;
-        
-        final Frustum f;
-        
-        boolean statusChecked;
-        final Surface surface;
-        
-        public GlslPass(Surface surface) {
-            this.surface = surface;
-            
-            shape = new Box(4f);
-            shader = new GlslShader();
-            
-            shader.setShader(ShaderType.VERTEX, VERTEX_SHADER);
-            shader.setShader(ShaderType.FRAGMENT, FRAGMENT_SHADER);
-            
-            f = new Frustum(60f, surface.getWidth() / (float) surface.getHeight(), 1f, 100f);
-            f.setOrientation(new Vector3(0f, 3f, 10f), new Vector3(0f, 0f, -1f), new Vector3(0f, 1f, 0f));
-            
-            int width = 256;
-            int height = 256;
-            int depth = 256;
-            
-            byte[] volumeBuffer = new byte[width * height * depth * 4];
-            for (int z = 0; z < depth; z++) {
-                for (int y = 0; y < height; y++) {
-                    for (int x = 0; x < width; x++) {
-                        int index = z * width * height * 4 + y * width * 4 + x * 4;
-
-                        volumeBuffer[index] = (byte) (z / (float) depth * 256);
-                        volumeBuffer[index + 1] = (byte) (y / (float) height * 256);
-                        volumeBuffer[index + 2] = (byte) (x / (float) width * 256);
-                        volumeBuffer[index + 3] = (byte) 127;
-                    }
-                }
-            }
-            
-            Mipmap data = new Mipmap(new BufferData(volumeBuffer), width, height, depth, TextureFormat.RGBA);
-            volume = new Texture(Target.T_3D, data);
-            volume.setFilter(Filter.NEAREST);
-        }
-