Commits

Michael Ludwig  committed ef74e0e

Rename controller packages to task, to mirror previous naming change in Entreri

  • Participants
  • Parent commits b8cdf4a

Comments (0)

Files changed (91)

File ferox-demos/src/main/java/com/ferox/physics/PhysicsApplicationStub.java

 import com.ferox.math.bounds.QuadTree;
 import com.ferox.physics.collision.CollisionBody;
 import com.ferox.physics.collision.DefaultCollisionAlgorithmProvider;
-import com.ferox.physics.controller.ConstraintSolvingTask;
-import com.ferox.physics.controller.ForcesTask;
-import com.ferox.physics.controller.MotionTask;
-import com.ferox.physics.controller.TemporalSAPCollisionController;
+import com.ferox.physics.task.ConstraintSolvingTask;
+import com.ferox.physics.task.ForcesTask;
+import com.ferox.physics.task.MotionTask;
+import com.ferox.physics.task.TemporalSAPCollisionTask;
 import com.ferox.renderer.OnscreenSurface;
 import com.ferox.renderer.impl.lwjgl.LwjglFramework;
 import com.ferox.scene.Camera;
 import com.ferox.scene.Transform;
-import com.ferox.scene.controller.BuildVisibilityIndexTask;
-import com.ferox.scene.controller.ComputeCameraFrustumTask;
-import com.ferox.scene.controller.ComputePVSTask;
-import com.ferox.scene.controller.UpdateWorldBoundsTask;
-import com.ferox.scene.controller.ffp.FixedFunctionRenderTask;
-import com.ferox.scene.controller.light.ComputeLightGroupTask;
-import com.ferox.scene.controller.light.ComputeShadowFrustumTask;
+import com.ferox.scene.task.BuildVisibilityIndexTask;
+import com.ferox.scene.task.ComputeCameraFrustumTask;
+import com.ferox.scene.task.ComputePVSTask;
+import com.ferox.scene.task.UpdateWorldBoundsTask;
+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.ApplicationStub;
 import com.ferox.util.profile.Profiler;
 import com.lhkbob.entreri.ComponentIterator;
                                       //                                                                                               6),
                                       //                                                                          new DefaultCollisionAlgorithmProvider()),
                                       //                                      new SingleAxisSAPCollisionController(new DefaultCollisionAlgorithmProvider()),
-                                      new TemporalSAPCollisionController(new DefaultCollisionAlgorithmProvider()),
+                                      new TemporalSAPCollisionTask(new DefaultCollisionAlgorithmProvider()),
                                       new ConstraintSolvingTask(), new MotionTask(),
                                       new TransformController());
 

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

 import com.ferox.scene.PointLight;
 import com.ferox.scene.Renderable;
 import com.ferox.scene.Transform;
-import com.ferox.scene.controller.BuildVisibilityIndexTask;
-import com.ferox.scene.controller.ComputeCameraFrustumTask;
-import com.ferox.scene.controller.ComputePVSTask;
-import com.ferox.scene.controller.UpdateWorldBoundsTask;
-import com.ferox.scene.controller.light.ComputeLightGroupTask;
-import com.ferox.scene.controller.light.ComputeShadowFrustumTask;
+import com.ferox.scene.task.BuildVisibilityIndexTask;
+import com.ferox.scene.task.ComputeCameraFrustumTask;
+import com.ferox.scene.task.ComputePVSTask;
+import com.ferox.scene.task.UpdateWorldBoundsTask;
+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;

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

 import com.ferox.scene.Renderable;
 import com.ferox.scene.Transform;
 import com.ferox.scene.Transparent;
-import com.ferox.scene.controller.BuildVisibilityIndexTask;
-import com.ferox.scene.controller.ComputeCameraFrustumTask;
-import com.ferox.scene.controller.ComputePVSTask;
-import com.ferox.scene.controller.UpdateWorldBoundsTask;
-import com.ferox.scene.controller.light.ComputeLightGroupTask;
-import com.ferox.scene.controller.light.ComputeShadowFrustumTask;
+import com.ferox.scene.task.BuildVisibilityIndexTask;
+import com.ferox.scene.task.ComputeCameraFrustumTask;
+import com.ferox.scene.task.ComputePVSTask;
+import com.ferox.scene.task.UpdateWorldBoundsTask;
+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.ApplicationStub;
 import com.ferox.util.geom.Box;
 import com.ferox.util.geom.Geometry;

File ferox-physics/src/main/java/com/ferox/physics/controller/BoundsResult.java

-package com.ferox.physics.controller;
-
-import com.ferox.math.AxisAlignedBox;
-import com.ferox.math.Const;
-import com.lhkbob.entreri.task.Result;
-
-public class BoundsResult extends Result {
-    private final AxisAlignedBox bounds;
-
-    public BoundsResult(@Const AxisAlignedBox bounds) {
-        this.bounds = new AxisAlignedBox(bounds);
-    }
-
-    @Const
-    public AxisAlignedBox getBounds() {
-        return bounds;
-    }
-
-    @Override
-    public boolean isSingleton() {
-        return true;
-    }
-}

File ferox-physics/src/main/java/com/ferox/physics/controller/CollisionTask.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.physics.controller;
-
-import java.util.Collections;
-import java.util.Set;
-
-import com.ferox.physics.collision.ClosestPair;
-import com.ferox.physics.collision.CollisionAlgorithm;
-import com.ferox.physics.collision.CollisionAlgorithmProvider;
-import com.ferox.physics.collision.CollisionBody;
-import com.ferox.physics.dynamics.ContactManifoldPool;
-import com.ferox.physics.dynamics.LinearConstraintPool;
-import com.ferox.physics.dynamics.RigidBody;
-import com.ferox.util.profile.Profiler;
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.task.ElapsedTimeResult;
-import com.lhkbob.entreri.task.Job;
-import com.lhkbob.entreri.task.ParallelAware;
-import com.lhkbob.entreri.task.Task;
-
-public abstract class CollisionTask implements Task {
-    private final CollisionAlgorithmProvider algorithms;
-
-    private final ContactManifoldPool manifolds;
-
-    private final LinearConstraintPool contactGroup;
-    private final LinearConstraintPool frictionGroup;
-
-    protected double dt;
-
-    public CollisionTask(CollisionAlgorithmProvider algorithms) {
-        if (algorithms == null) {
-            throw new NullPointerException("Algorithm provider cannot be null");
-        }
-
-        this.algorithms = algorithms;
-        manifolds = new ContactManifoldPool();
-        contactGroup = new LinearConstraintPool(null);
-        frictionGroup = new LinearConstraintPool(contactGroup);
-    }
-
-    public void report(ElapsedTimeResult dt) {
-        this.dt = dt.getTimeDelta();
-    }
-
-    @Override
-    public Task process(EntitySystem system, Job job) {
-        return new WarmstartTask();
-    }
-
-    @Override
-    public void reset(EntitySystem system) {
-        if (manifolds.getEntitySystem() != system) {
-            manifolds.setEntitySystem(system);
-        }
-
-        // reset constraint pools
-        contactGroup.clear();
-        frictionGroup.clear();
-    }
-
-    protected void reportConstraints(Job job) {
-        manifolds.generateConstraints(dt, contactGroup, frictionGroup);
-        job.report(new ConstraintResult(contactGroup));
-        job.report(new ConstraintResult(frictionGroup));
-    }
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    protected void notifyPotentialContact(CollisionBody bodyA, CollisionBody bodyB) {
-        // test collision groups and masks
-        if (!bodyA.canCollide(bodyB)) {
-            return;
-        }
-        // collisions must have at least one rigid body to act on
-        if (bodyA.getEntity().get(RigidBody.class) == null && bodyB.getEntity()
-                                                                   .get(RigidBody.class) == null) {
-            return;
-        }
-
-        // get the appropriate algorithm
-        CollisionAlgorithm algorithm = algorithms.getAlgorithm(bodyA.getShape()
-                                                                    .getClass(),
-                                                               bodyB.getShape()
-                                                                    .getClass());
-
-        if (algorithm != null) {
-            // compute closest pair between the two shapes
-            ClosestPair pair = algorithm.getClosestPair(bodyA.getShape(),
-                                                        bodyA.getTransform(),
-                                                        bodyB.getShape(),
-                                                        bodyB.getTransform());
-
-            if (pair != null && pair.isIntersecting()) {
-                // add to manifold only when there is an intersection
-                manifolds.addContact(bodyA, bodyB, pair);
-            }
-        }
-    }
-
-    private class WarmstartTask implements Task, ParallelAware {
-        @Override
-        public Set<Class<? extends ComponentData<?>>> getAccessedComponents() {
-            return Collections.emptySet();
-        }
-
-        @Override
-        public boolean isEntitySetModified() {
-            return false;
-        }
-
-        @Override
-        public Task process(EntitySystem system, Job job) {
-            // read back computed impulses from constraint solving controller
-            Profiler.push("warmstart-contacts");
-            manifolds.computeWarmstartImpulses(contactGroup, frictionGroup);
-            Profiler.pop();
-            return null;
-        }
-
-        @Override
-        public void reset(EntitySystem system) {
-
-        }
-    }
-}

File ferox-physics/src/main/java/com/ferox/physics/controller/ConstraintResult.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.physics.controller;
-
-import com.ferox.physics.dynamics.LinearConstraintPool;
-import com.lhkbob.entreri.task.Result;
-
-public class ConstraintResult extends Result {
-    private final LinearConstraintPool group;
-
-    public ConstraintResult(LinearConstraintPool group) {
-        if (group == null) {
-            throw new NullPointerException("LinearConstraintPool cannot be null");
-        }
-        this.group = group;
-    }
-
-    public LinearConstraintPool getConstraints() {
-        return group;
-    }
-
-    @Override
-    public boolean isSingleton() {
-        return false;
-    }
-}

File ferox-physics/src/main/java/com/ferox/physics/controller/ConstraintSolvingTask.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.physics.controller;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import com.ferox.math.Vector3;
-import com.ferox.math.entreri.Vector3Property;
-import com.ferox.physics.dynamics.LinearConstraintPool;
-import com.ferox.physics.dynamics.LinearConstraintSolver;
-import com.ferox.physics.dynamics.RigidBody;
-import com.ferox.util.profile.Profiler;
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.task.Job;
-import com.lhkbob.entreri.task.ParallelAware;
-import com.lhkbob.entreri.task.Task;
-
-public class ConstraintSolvingTask implements Task, ParallelAware {
-    private final LinearConstraintSolver solver;
-
-    private List<LinearConstraintPool> groups;
-
-    private Vector3Property deltaLinearImpulse;
-    private Vector3Property deltaAngularImpulse;
-
-    // instances used locally but instantiated once to save performance
-    private RigidBody rigidBody;
-    private ComponentIterator iterator;
-    private final Vector3 delta = new Vector3();
-
-    public ConstraintSolvingTask() {
-        solver = new LinearConstraintSolver();
-    }
-
-    @Override
-    public void reset(EntitySystem system) {
-        if (rigidBody == null) {
-            rigidBody = system.createDataInstance(RigidBody.class);
-            iterator = new ComponentIterator(system);
-            iterator.addRequired(rigidBody);
-
-            deltaLinearImpulse = system.decorate(RigidBody.class,
-                                                 new Vector3Property.Factory(new Vector3()));
-            deltaAngularImpulse = system.decorate(RigidBody.class,
-                                                  new Vector3Property.Factory(new Vector3()));
-
-            solver.setDeltaLinearImpulseProperty(deltaLinearImpulse);
-            solver.setDeltaAngularImpulseProperty(deltaAngularImpulse);
-        }
-
-        groups = new ArrayList<LinearConstraintPool>();
-        iterator.reset();
-    }
-
-    @Override
-    public Task process(EntitySystem system, Job job) {
-        Profiler.push("constraint-solving-task");
-
-        Profiler.push("solve-constraints");
-        LinearConstraintPool[] asArray = groups.toArray(new LinearConstraintPool[groups.size()]);
-        solver.solve(asArray);
-        Profiler.pop();
-
-        // now apply all of the delta impulses back to the rigid bodies
-        Profiler.push("apply-constraints");
-        while (iterator.next()) {
-            // linear velocity
-            deltaLinearImpulse.get(rigidBody.getIndex(), delta);
-            rigidBody.setVelocity(delta.add(rigidBody.getVelocity()));
-
-            // angular velocity
-            deltaAngularImpulse.get(rigidBody.getIndex(), delta);
-            rigidBody.setAngularVelocity(delta.add(rigidBody.getAngularVelocity()));
-
-            // 0 out delta impulse for next frame
-            delta.set(0, 0, 0);
-            deltaLinearImpulse.set(delta, rigidBody.getIndex());
-            deltaAngularImpulse.set(delta, rigidBody.getIndex());
-        }
-        Profiler.pop();
-        Profiler.pop();
-
-        return null;
-    }
-
-    public void report(ConstraintResult r) {
-        groups.add(r.getConstraints());
-    }
-
-    @Override
-    public Set<Class<? extends ComponentData<?>>> getAccessedComponents() {
-        return Collections.<Class<? extends ComponentData<?>>> singleton(RigidBody.class);
-    }
-
-    @Override
-    public boolean isEntitySetModified() {
-        return false;
-    }
-}

File ferox-physics/src/main/java/com/ferox/physics/controller/ForcesTask.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.physics.controller;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import com.ferox.math.Const;
-import com.ferox.math.Matrix3;
-import com.ferox.math.Matrix4;
-import com.ferox.math.Vector3;
-import com.ferox.physics.collision.CollisionBody;
-import com.ferox.physics.dynamics.ExplicitEulerIntegrator;
-import com.ferox.physics.dynamics.Gravity;
-import com.ferox.physics.dynamics.Integrator;
-import com.ferox.physics.dynamics.RigidBody;
-import com.ferox.util.profile.Profiler;
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.task.ElapsedTimeResult;
-import com.lhkbob.entreri.task.Job;
-import com.lhkbob.entreri.task.ParallelAware;
-import com.lhkbob.entreri.task.Task;
-
-// FIXME this task should be split into 3 pieces:
-// 1. inertia tensor computer (which must be done before any force can be added to a body)
-// 2. gravity force applier
-// 3. force integrator
-// Although see note in SpatialIndexCollisionController. This is the main place
-// that benefits from merging the loop -> maybe I can artificially support
-// auto-merging by calling this the PrepTask that takes preparers that operate
-// on single RigidBody/CollisionBody entities. These can be split into 
-// composable tasks but only iterated through once in this task.
-//
-// The Motion and ConstraintSolving tasks could be similarly combined, although
-// part of the constraint solving task is global... I bet it doesn't make a 
-// big performance dent so let's just keep it in mind but design for maximum
-// cleanliness right now.
-public class ForcesTask implements Task, ParallelAware {
-    private static final Set<Class<? extends ComponentData<?>>> COMPONENTS;
-    static {
-        Set<Class<? extends ComponentData<?>>> types = new HashSet<Class<? extends ComponentData<?>>>();
-        types.add(CollisionBody.class);
-        types.add(RigidBody.class);
-        types.add(Gravity.class);
-        COMPONENTS = Collections.unmodifiableSet(types);
-    }
-
-    private final Integrator integrator;
-    private final Vector3 defaultGravity;
-
-    private double dt;
-
-    // cached instances that could be local to process()
-    private RigidBody rigidBody;
-    private CollisionBody colBody;
-    private Gravity gravity;
-    private ComponentIterator iterator;
-
-    private final Vector3 inertia = new Vector3();
-    private final Vector3 force = new Vector3();
-    private final Matrix3 rotation = new Matrix3();
-
-    public ForcesTask() {
-        this(new Vector3(0, -9.8, 0));
-    }
-
-    public ForcesTask(@Const Vector3 gravity) {
-        this(gravity, new ExplicitEulerIntegrator());
-    }
-
-    public ForcesTask(@Const Vector3 gravity, Integrator integrator) {
-        if (integrator == null) {
-            throw new NullPointerException("Integrator cannot be null");
-        }
-        defaultGravity = gravity.clone();
-        this.integrator = integrator;
-    }
-
-    public void report(ElapsedTimeResult dt) {
-        this.dt = dt.getTimeDelta();
-    }
-
-    @Override
-    public void reset(EntitySystem system) {
-        if (rigidBody == null) {
-            rigidBody = system.createDataInstance(RigidBody.class);
-            colBody = system.createDataInstance(CollisionBody.class);
-            gravity = system.createDataInstance(Gravity.class);
-
-            iterator = new ComponentIterator(system).addRequired(rigidBody)
-                                                    .addRequired(colBody)
-                                                    .addOptional(gravity);
-        }
-
-        iterator.reset();
-    }
-
-    @Override
-    public Task process(EntitySystem system, Job job) {
-        Profiler.push("apply-forces");
-        while (iterator.next()) {
-            Matrix4 transform = colBody.getTransform();
-
-            // compute the body's new inertia tensor
-            Matrix3 tensor = rigidBody.getInertiaTensorInverse();
-
-            colBody.getShape().getInertiaTensor(rigidBody.getMass(), inertia);
-            inertia.set(1.0 / inertia.x, 1.0 / inertia.y, 1.0 / inertia.z);
-
-            rotation.setUpper(transform);
-            tensor.mulDiagonal(rotation, inertia).mulTransposeRight(rotation);
-            rigidBody.setInertiaTensorInverse(tensor);
-
-            // add gravity force
-            if (gravity.isEnabled()) {
-                force.scale(gravity.getGravity(), rigidBody.getMass());
-            } else {
-                force.scale(defaultGravity, rigidBody.getMass());
-            }
-
-            rigidBody.addForce(force, null);
-
-            // integrate and apply forces to the body's velocity
-            Vector3 lv = rigidBody.getVelocity();
-            integrator.integrateLinearAcceleration(force.scale(rigidBody.getTotalForce(),
-                                                               rigidBody.getInverseMass()),
-                                                   dt, lv);
-            rigidBody.setVelocity(lv);
-
-            Vector3 la = rigidBody.getAngularVelocity();
-            integrator.integrateAngularAcceleration(force.mul(tensor,
-                                                              rigidBody.getTotalTorque()),
-                                                    dt, la);
-            rigidBody.setAngularVelocity(la);
-
-            // reset forces
-            rigidBody.clearForces();
-        }
-        Profiler.pop();
-        return null;
-    }
-
-    @Override
-    public Set<Class<? extends ComponentData<?>>> getAccessedComponents() {
-        return COMPONENTS;
-    }
-
-    @Override
-    public boolean isEntitySetModified() {
-        return false;
-    }
-}

File ferox-physics/src/main/java/com/ferox/physics/controller/MotionTask.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.physics.controller;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import com.ferox.math.Const;
-import com.ferox.math.Matrix3;
-import com.ferox.math.Matrix4;
-import com.ferox.math.Vector3;
-import com.ferox.physics.collision.CollisionBody;
-import com.ferox.physics.dynamics.ExplicitEulerIntegrator;
-import com.ferox.physics.dynamics.Integrator;
-import com.ferox.physics.dynamics.RigidBody;
-import com.ferox.util.profile.Profiler;
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.task.ElapsedTimeResult;
-import com.lhkbob.entreri.task.Job;
-import com.lhkbob.entreri.task.ParallelAware;
-import com.lhkbob.entreri.task.Task;
-
-// FIXME: this can be moved to the beginning, running before other per-collision/rigidbody
-// preparation actions. Thus the new code would look like:
-// Prep task:
-// 1. Integrate velocities from previous frame into transform
-// 2. Compute tensor
-// 3. Compute new world bounds per entity, and entire union
-// 4. Add gravity + other forces (sep. actions)
-// 5. Integrate forces into velocities
-// Collision task:
-// 1. Rebuild + resize spatial index
-// 2. Do broadphase + collision detection
-// 3. Generate contact constraints from manifolds
-// Constraint task:
-// 1. Solve all constraints
-// 2. Update velocities from computed deltas, and reset deltas
-// Will wait to do this until after scene rendering is done, so I can compare speed boost
-// I'll keep it even if it's not faster (so long as not too slow), because I think
-// the workflow is cleaner
-
-public class MotionTask implements Task, ParallelAware {
-    private static final Set<Class<? extends ComponentData<?>>> COMPONENTS;
-    static {
-        Set<Class<? extends ComponentData<?>>> types = new HashSet<Class<? extends ComponentData<?>>>();
-        types.add(CollisionBody.class);
-        types.add(RigidBody.class);
-        COMPONENTS = Collections.unmodifiableSet(types);
-    }
-
-    private final Integrator integrator;
-
-    private double dt;
-
-    // values that could be local to process(), but don't need to be allocated
-    // every frame
-    private final Vector3 predictedPosition = new Vector3();
-    private final Matrix3 predictedRotation = new Matrix3();
-
-    private RigidBody rigidBody;
-    private CollisionBody collisionBody;
-    private ComponentIterator iterator;
-
-    public MotionTask() {
-        this(new ExplicitEulerIntegrator());
-    }
-
-    public MotionTask(Integrator integrator) {
-        if (integrator == null) {
-            throw new NullPointerException("Integrator can't be null");
-        }
-        this.integrator = integrator;
-    }
-
-    public void report(ElapsedTimeResult dt) {
-        this.dt = dt.getTimeDelta();
-    }
-
-    @Override
-    public void reset(EntitySystem system) {
-        if (iterator == null) {
-            rigidBody = system.createDataInstance(RigidBody.class);
-            collisionBody = system.createDataInstance(CollisionBody.class);
-            iterator = new ComponentIterator(system).addRequired(rigidBody)
-                                                    .addRequired(collisionBody);
-        } else {
-            iterator.reset();
-        }
-    }
-
-    @Override
-    public Task process(EntitySystem system, Job job) {
-        Profiler.push("integrate-motion");
-        while (iterator.next()) {
-            Matrix4 transform = collisionBody.getTransform();
-
-            predictedRotation.setUpper(transform);
-            predictedPosition.set(transform.m03, transform.m13, transform.m23);
-
-            integrator.integrateLinearVelocity(rigidBody.getVelocity(), dt,
-                                               predictedPosition);
-            integrator.integrateAngularVelocity(rigidBody.getAngularVelocity(), dt,
-                                                predictedRotation);
-
-            // push values back into transform
-            setTransform(predictedRotation, predictedPosition, transform);
-
-            collisionBody.setTransform(transform);
-        }
-        Profiler.pop();
-
-        return null;
-    }
-
-    private void setTransform(@Const Matrix3 r, @Const Vector3 p, Matrix4 t) {
-        t.setUpper(r);
-
-        t.m03 = p.x;
-        t.m13 = p.y;
-        t.m23 = p.z;
-
-        // ensure this is still an affine transform
-        t.m30 = 0.0;
-        t.m31 = 0.0;
-        t.m32 = 0.0;
-        t.m33 = 1.0;
-    }
-
-    @Override
-    public Set<Class<? extends ComponentData<?>>> getAccessedComponents() {
-        return COMPONENTS;
-    }
-
-    @Override
-    public boolean isEntitySetModified() {
-        return false;
-    }
-}

File ferox-physics/src/main/java/com/ferox/physics/controller/PrepareAction.java

-package com.ferox.physics.controller;
-
-import com.ferox.physics.collision.CollisionBody;
-import com.ferox.physics.dynamics.RigidBody;
-import com.lhkbob.entreri.task.Job;
-
-public interface PrepareAction {
-    public boolean prePrepare(Job job);
-
-    public void prepare(Job job, CollisionBody collisonBody, RigidBody rigidBody);
-
-    public void postPrepare(Job job);
-}

File ferox-physics/src/main/java/com/ferox/physics/controller/SimulationPreparationTask.java

-package com.ferox.physics.controller;
-
-public class SimulationPreparationTask {
-
-}

File ferox-physics/src/main/java/com/ferox/physics/controller/SingleAxisSAPCollisionController.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.physics.controller;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import com.ferox.math.AxisAlignedBox;
-import com.ferox.math.Functions;
-import com.ferox.physics.collision.CollisionAlgorithmProvider;
-import com.ferox.physics.collision.CollisionBody;
-import com.ferox.physics.dynamics.RigidBody;
-import com.ferox.util.Bag;
-import com.ferox.util.profile.Profiler;
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.Entity;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.task.Job;
-import com.lhkbob.entreri.task.ParallelAware;
-import com.lhkbob.entreri.task.Task;
-
-public class SingleAxisSAPCollisionController extends CollisionTask implements ParallelAware {
-    private static final Set<Class<? extends ComponentData<?>>> COMPONENTS;
-    static {
-        Set<Class<? extends ComponentData<?>>> types = new HashSet<Class<? extends ComponentData<?>>>();
-        types.add(CollisionBody.class);
-        types.add(RigidBody.class);
-        COMPONENTS = Collections.unmodifiableSet(types);
-    }
-
-    private final Bag<Entity> bodies;
-
-    // cached instances that are normally local to process()
-    private CollisionBody bodyA;
-    private CollisionBody bodyB;
-
-    private ComponentIterator iterator;
-
-    public SingleAxisSAPCollisionController(CollisionAlgorithmProvider algorithms) {
-        super(algorithms);
-        bodies = new Bag<Entity>();
-    }
-
-    @Override
-    public void reset(EntitySystem system) {
-        super.reset(system);
-
-        if (bodyA == null) {
-            bodyA = system.createDataInstance(CollisionBody.class);
-            bodyB = system.createDataInstance(CollisionBody.class);
-
-            iterator = new ComponentIterator(system).addRequired(bodyA);
-        }
-
-        iterator.reset();
-        bodies.clear(true);
-    }
-
-    @Override
-    public Task process(EntitySystem system, Job job) {
-        Profiler.push("detect-collisions");
-
-        // build up axis lists to sort
-        Profiler.push("prepare-axis");
-        while (iterator.next()) {
-            bodies.add(bodyA.getEntity());
-        }
-
-        int[] edges = new int[bodies.size() * 2];
-        int[] edgeLabels = new int[bodies.size() * 2];
-
-        for (int i = 0; i < bodies.size(); i++) {
-            bodies.get(i).get(bodyA);
-            AxisAlignedBox aabb = bodyA.getWorldBounds();
-            edges[(i << 1)] = Functions.sortableFloatToIntBits((float) aabb.min.x);
-            edges[(i << 1) + 1] = Functions.sortableFloatToIntBits((float) aabb.max.x);
-            edgeLabels[(i << 1)] = i;
-            edgeLabels[(i << 1) + 1] = (0x80000000) | i;
-        }
-        Profiler.pop();
-
-        // sort edges, keeping edgeLabels in sync with swaps
-        Profiler.push("sort-axis");
-        quickSort(edges, edgeLabels, 0, edges.length);
-        Profiler.pop();
-
-        // iterate edges, checking overlapping pairs
-        Profiler.push("prune");
-        int openIndex = -1;
-        int nextIndex = -1;
-
-        int currLabel;
-        boolean isMax;
-        for (int i = 0; i < edges.length; i++) {
-            // check the next label
-            isMax = edgeLabels[i] < 0;
-            currLabel = (~0x80000000) & edgeLabels[i];
-
-            if (isMax) {
-                if (openIndex >= 0 && currLabel == edgeLabels[openIndex]) {
-                    // reached the end of the current box, update loop if
-                    // we know of another min edge to visit
-                    if (nextIndex >= 0) {
-                        i = nextIndex;
-                    }
-
-                    openIndex = nextIndex;
-                    nextIndex = -1;
-                }
-                // otherwise we're skipping past the max edges of already
-                // processed boxes
-            } else {
-                if (openIndex < 0) {
-                    // open another box
-                    openIndex = i;
-                } else {
-                    // perform intersection test with open box and current box
-                    bodies.get(edgeLabels[openIndex]).get(bodyA);
-                    bodies.get(currLabel).get(bodyB);
-
-                    if (bodyA.getWorldBounds().intersects(bodyB.getWorldBounds())) {
-                        notifyPotentialContact(bodyA, bodyB);
-                    }
-
-                    if (nextIndex < 0) {
-                        // earliest next min edge we've found
-                        nextIndex = i;
-                    }
-                }
-            }
-        }
-        Profiler.pop();
-
-        // generate constraints
-        Profiler.push("generate-constraints");
-        reportConstraints(job);
-        Profiler.pop();
-
-        Profiler.pop();
-        return super.process(system, job);
-    }
-
-    @Override
-    public Set<Class<? extends ComponentData<?>>> getAccessedComponents() {
-        return COMPONENTS;
-    }
-
-    @Override
-    public boolean isEntitySetModified() {
-        return false;
-    }
-
-    // use quick sort to sort elements in x, swapping y along with it
-    private void quickSort(int[] x, int[] y, int off, int len) {
-        // insertion sort on smallest arrays
-        if (len < 7) {
-            for (int i = off; i < len + off; i++) {
-                for (int j = i; j > off && x[j - 1] > x[j]; j--) {
-                    swap(x, y, j, j - 1);
-                }
-            }
-            return;
-        }
-
-        // choose a partition element, v
-        int m = off + (len >> 1); // small arrays, middle element
-        if (len > 7) {
-            int l = off;
-            int n = off + len - 1;
-            if (len > 40) { // big arrays, pseudomedian of 9
-                int s = len / 8;
-                l = med3(x, l, l + s, l + 2 * s);
-                m = med3(x, m - s, m, m + s);
-                n = med3(x, n - 2 * s, n - s, n);
-            }
-            m = med3(x, l, m, n); // mid-size, med of 3
-        }
-        int v = x[m];
-
-        int a = off, b = a, c = off + len - 1, d = c;
-        while (true) {
-            while (b <= c && x[b] <= v) {
-                if (v == x[b]) {
-                    swap(x, y, a++, b);
-                }
-                b++;
-            }
-            while (c >= b && x[c] >= v) {
-                if (v == x[c]) {
-                    swap(x, y, c, d--);
-                }
-                c--;
-            }
-            if (b > c) {
-                break;
-            }
-            swap(x, y, b++, c--);
-        }
-
-        // swap partition elements back to middle
-        int s, n = off + len;
-        s = Math.min(a - off, b - a);
-        vecswap(x, y, off, b - s, s);
-        s = Math.min(d - c, n - d - 1);
-        vecswap(x, y, b, n - s, s);
-
-        // recursively sort non-partition-elements
-        if ((s = b - a) > 1) {
-            quickSort(x, y, off, s);
-        }
-        if ((s = d - c) > 1) {
-            quickSort(x, y, n - s, s);
-        }
-    }
-
-    // swaps the elements at indices a and b, along with the hashes in x
-    private void swap(int[] x, int[] y, int a, int b) {
-        int k = x[a];
-        x[a] = x[b];
-        x[b] = k;
-
-        int l = y[a];
-        y[a] = y[b];
-        y[b] = l;
-    }
-
-    // swaps n elements starting at a and b, such that (a,b), (a+1, b+1), etc. are swapped
-    private void vecswap(int[] x, int[] y, int a, int b, int n) {
-        for (int i = 0; i < n; i++, a++, b++) {
-            swap(x, y, a, b);
-        }
-    }
-
-    // returns the index of the median of the three indexed elements
-    private static int med3(int[] x, int a, int b, int c) {
-        return (x[a] < x[b] ? (x[b] < x[c] ? b : x[a] < x[c] ? c : a) : (x[b] > x[c] ? b : x[a] > x[c] ? c : a));
-    }
-}

File ferox-physics/src/main/java/com/ferox/physics/controller/SpatialIndexCollisionController.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.physics.controller;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import com.ferox.math.AxisAlignedBox;
-import com.ferox.math.bounds.BoundedSpatialIndex;
-import com.ferox.math.bounds.IntersectionCallback;
-import com.ferox.math.bounds.SpatialIndex;
-import com.ferox.physics.collision.CollisionAlgorithmProvider;
-import com.ferox.physics.collision.CollisionBody;
-import com.ferox.physics.dynamics.RigidBody;
-import com.ferox.util.profile.Profiler;
-import com.lhkbob.entreri.ComponentData;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.Entity;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.task.Job;
-import com.lhkbob.entreri.task.ParallelAware;
-import com.lhkbob.entreri.task.Task;
-
-public class SpatialIndexCollisionController extends CollisionTask implements ParallelAware {
-    private static final Set<Class<? extends ComponentData<?>>> COMPONENTS;
-    static {
-        Set<Class<? extends ComponentData<?>>> types = new HashSet<Class<? extends ComponentData<?>>>();
-        types.add(CollisionBody.class);
-        types.add(RigidBody.class);
-        COMPONENTS = Collections.unmodifiableSet(types);
-    }
-
-    private final SpatialIndex<Entity> index;
-
-    // cached instances that are normally local to process()
-    private CollisionBody bodyA;
-    private CollisionBody bodyB;
-
-    private ComponentIterator iterator;
-
-    public SpatialIndexCollisionController(SpatialIndex<Entity> index,
-                                           CollisionAlgorithmProvider algorithms) {
-        super(algorithms);
-        if (index == null) {
-            throw new NullPointerException("SpatialIndex cannot be null");
-        }
-        this.index = index;
-    }
-
-    @Override
-    public void reset(EntitySystem system) {
-        super.reset(system);
-
-        if (bodyA == null) {
-            bodyA = system.createDataInstance(CollisionBody.class);
-            bodyB = system.createDataInstance(CollisionBody.class);
-
-            iterator = new ComponentIterator(system).addRequired(bodyA);
-        }
-
-        index.clear(true);
-        iterator.reset();
-    }
-
-    // NOTE: with regards to performance and task creation, there are 
-    // non-zero costs with iterating through the system: content is pulled
-    // from the properties into local instances and then processed (either
-    // directly with getters, or the onSet() method). This means that if
-    // multiple tasks are executed consecutively but access the same component
-    // types, we're doing additional load work.
-    //
-    // It'd be faster to merge those tasks into a single loop. Is this something
-    // I should automate, or something that I should consider when I design my
-    // tasks? I think it's too awkward to automate, although it would be cool.
-    //
-    // 
-
-    @Override
-    public Task process(EntitySystem system, Job job) {
-        Profiler.push("detect-collisions");
-
-        // if the index is bounded, update its size so everything is processed
-        if (index instanceof BoundedSpatialIndex) {
-            // FIXME how much does computing the union hurt our performance?
-            // FIXME do we want to shrink the extent even when the original extent
-            // is large enough? How does that affect query performance?
-
-            // FIXME right now, setTransform() computes updateBounds() for CollisionBody,
-            // which isn't the best -> what if we had a task that just computed
-            // world bounds (like world bounds task in scene module) and it could
-            // report the union as well.
-
-            // Now the only issue is whether we want to duplicate this code, since
-            // both sources need the same functionality but they are definitely not
-            // sharable.  The only place would be if the math module defined the
-            // world bounds result type (we need different tasks because they
-            // process things differently anyways).
-            Profiler.push("update-index-extent");
-            AxisAlignedBox extent = new AxisAlignedBox();
-            boolean first = true;
-            while (iterator.next()) {
-                if (first) {
-                    extent.set(bodyA.getWorldBounds());
-                    first = false;
-                } else {
-                    extent.union(bodyA.getWorldBounds());
-                }
-            }
-
-            // make the extents slightly larger so floating point errors don't
-            // cause shapes on the edge to get discarded
-            extent.min.scale(1.1);
-            extent.max.scale(1.1);
-            ((BoundedSpatialIndex<Entity>) index).setExtent(extent);
-            iterator.reset();
-            Profiler.pop();
-        }
-
-        // fill the index with all collision bodies
-        Profiler.push("build-index");
-        while (iterator.next()) {
-            index.add(bodyA.getEntity(), bodyA.getWorldBounds());
-        }
-        Profiler.pop();
-
-        // query for all intersections
-        Profiler.push("find-intersections");
-        index.query(new CollisionCallback(bodyA, bodyB));
-        Profiler.pop();
-
-        Profiler.push("generate-constraints");
-        reportConstraints(job);
-        Profiler.pop();
-
-        Profiler.pop();
-        return super.process(system, job);
-    }
-
-    private class CollisionCallback implements IntersectionCallback<Entity> {
-        private final CollisionBody bodyA;
-        private final CollisionBody bodyB;
-
-        public CollisionCallback(CollisionBody bodyA, CollisionBody bodyB) {
-            this.bodyA = bodyA;
-            this.bodyB = bodyB;
-        }
-
-        @Override
-        public void process(Entity a, AxisAlignedBox boundsA, Entity b,
-                            AxisAlignedBox boundsB) {
-            // at this point we know the world bounds of a and b intersect, but
-            // we need to test for collision against their actual shapes
-            a.get(bodyA);
-            b.get(bodyB);
-
-            notifyPotentialContact(bodyA, bodyB);
-        }
-    }
-
-    @Override
-    public Set<Class<? extends ComponentData<?>>> getAccessedComponents() {
-        return COMPONENTS;
-    }
-
-    @Override
-    public boolean isEntitySetModified() {
-        return false;
-    }
-}

File ferox-physics/src/main/java/com/ferox/physics/controller/TemporalSAPCollisionController.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.physics.controller;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-import com.ferox.math.AxisAlignedBox;
-import com.ferox.math.Functions;
-import com.ferox.physics.collision.CollisionAlgorithmProvider;
-import com.ferox.physics.collision.CollisionBody;
-import com.ferox.physics.collision.CollisionPair;
-import com.ferox.util.profile.Profiler;
-import com.lhkbob.entreri.Component;
-import com.lhkbob.entreri.ComponentIterator;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.property.IntProperty;
-import com.lhkbob.entreri.task.Job;
-import com.lhkbob.entreri.task.Task;
-
-public class TemporalSAPCollisionController extends CollisionTask {
-    private final EdgeStore[] edges;
-    private final Set<CollisionPair> overlappingPairCache;
-
-    // cached local instances
-    private CollisionBody bodyA;
-    private CollisionBody bodyB;
-    private ComponentIterator bodyIterator; // iterates bodyA
-
-    public TemporalSAPCollisionController(CollisionAlgorithmProvider algorithms) {
-        super(algorithms);
-
-        overlappingPairCache = new HashSet<CollisionPair>();
-
-        edges = new EdgeStore[3];
-        for (int i = 0; i < edges.length; i++) {
-            edges[i] = new EdgeStore(overlappingPairCache);
-        }
-
-        // link edge axis
-        edges[0].axis1 = edges[1];
-        edges[0].axis2 = edges[2];
-        edges[1].axis1 = edges[2];
-        edges[1].axis2 = edges[0];
-        edges[2].axis1 = edges[0];
-        edges[2].axis2 = edges[1];
-    }
-
-    @Override
-    public void reset(EntitySystem system) {
-        super.reset(system);
-
-        if (bodyA == null) {
-            bodyA = system.createDataInstance(CollisionBody.class);
-            bodyB = system.createDataInstance(CollisionBody.class);
-            bodyIterator = new ComponentIterator(system).addRequired(bodyA);
-
-            for (int i = 0; i < edges.length; i++) {
-                edges[i].maxBodyEdges = system.decorate(CollisionBody.class,
-                                                        new IntProperty.Factory(-1));
-                edges[i].minBodyEdges = system.decorate(CollisionBody.class,
-                                                        new IntProperty.Factory(-1));
-            }
-        }
-
-        bodyIterator.reset();
-    }
-
-    @Override
-    public Task process(EntitySystem system, Job job) {
-        Profiler.push("detect-collisions");
-
-        // update all edges, sorting as necessary, and keeping track of
-        // overlapping pairs
-        Profiler.push("update-overlaps");
-        for (int i = 0; i < edges.length; i++) {
-            edges[i].removeDeadEdges();
-        }
-
-        while (bodyIterator.next()) {
-            AxisAlignedBox aabb = bodyA.getWorldBounds();
-            Component<CollisionBody> c = bodyA.getComponent();
-
-            if (edges[0].minBodyEdges.get(bodyA.getIndex()) < 0) {
-                // add body to edge lists
-                edges[0].addEdges(c, aabb.min.x, aabb.max.x, false);
-                edges[1].addEdges(c, aabb.min.y, aabb.max.y, false);
-                edges[2].addEdges(c, aabb.min.z, aabb.max.z, true);
-            } else {
-                edges[0].updateEdges(c, aabb.min.x, aabb.max.x);
-                edges[1].updateEdges(c, aabb.min.y, aabb.max.y);
-                edges[2].updateEdges(c, aabb.min.z, aabb.max.z);
-            }
-        }
-        Profiler.pop();
-
-        // iterate through pairs, removing those that have dead components,
-        // and performing narrow-phase collisions on the remaining
-        Profiler.push("process-overlaps");
-        for (CollisionPair pair : overlappingPairCache) {
-            if (bodyA.set(pair.getBodyA()) && bodyB.set(pair.getBodyB()) && bodyA.isEnabled() && bodyB.isEnabled()) {
-                // both components are still valid
-                notifyPotentialContact(bodyA, bodyB);
-            }
-        }
-        Profiler.pop();
-
-        // report constraints for accumulated collisions
-        Profiler.push("generate-constraints");
-        reportConstraints(job);
-        Profiler.pop();
-
-        Profiler.pop();
-
-        return super.process(system, job);
-    }
-
-    private static class EdgeStore {
-        private final Set<CollisionPair> overlappingPairCache;
-        private final CollisionPair query; // mutable, don't put in set
-
-        private int[] edges;
-        private Component<CollisionBody>[] edgeLabels;
-        private boolean[] edgeIsMax;
-
-        private IntProperty minBodyEdges;
-        private IntProperty maxBodyEdges;
-
-        private int edgeCount;
-
-        private EdgeStore axis1;
-        private EdgeStore axis2;
-
-        @SuppressWarnings("unchecked")
-        public EdgeStore(Set<CollisionPair> overlappingPairCache) {
-            this.overlappingPairCache = overlappingPairCache;
-            query = new CollisionPair();
-
-            edges = new int[2];
-            edgeLabels = new Component[2];
-            edgeIsMax = new boolean[2];
-
-            edgeCount = 0;
-
-            // sentinel values
-            edges[0] = Integer.MIN_VALUE;
-            edges[1] = Integer.MAX_VALUE;
-        }
-
-        public void removeDeadEdges() {
-            // remove disabled component edges by shifting everything to the left
-            for (int i = 1; i < 1 + edgeCount; i++) {
-                if (!edgeLabels[i].isEnabled()) {
-                    System.arraycopy(edgeLabels, i + 1, edgeLabels, i, edgeCount - i);
-                    System.arraycopy(edges, i + 1, edges, i, edgeCount - i);
-                    System.arraycopy(edgeIsMax, i + 1, edges, i, edgeCount - i);
-                    edgeCount--;
-
-                    if (edgeLabels[i].isLive()) {
-                        // still have component data so invalidate edge index
-                        maxBodyEdges.set(-1, edgeLabels[i].getIndex());
-                        minBodyEdges.set(-1, edgeLabels[i].getIndex());
-                    }
-                }
-            }
-
-            // sync component edge index after everything has been moved
-            for (int i = 1; i < 1 + edgeCount; i++) {
-                if (edgeIsMax[i]) {
-                    maxBodyEdges.set(i, edgeLabels[i].getIndex());
-                } else {
-                    minBodyEdges.set(i, edgeLabels[i].getIndex());
-                }
-            }
-        }
-
-        public void addEdges(Component<CollisionBody> body, double min, double max,
-                             boolean updateOverlaps) {
-            // offset by 1 for sentinel value stored in 0th index
-            int minIndex = 1 + edgeCount++;
-            int maxIndex = 1 + edgeCount++;
-            if (maxIndex >= edges.length - 2) {
-                int newLength = (edges.length) * 2 + 2;
-                edges = Arrays.copyOf(edges, newLength);
-                edgeLabels = Arrays.copyOf(edgeLabels, newLength);
-                edgeIsMax = Arrays.copyOf(edgeIsMax, newLength);
-            }
-
-            edges[minIndex] = Functions.sortableFloatToIntBits((float) min);
-            edgeIsMax[minIndex] = false;
-            edgeLabels[minIndex] = body;
-
-            edges[maxIndex] = Functions.sortableFloatToIntBits((float) max);
-            edgeIsMax[maxIndex] = true;
-            edgeLabels[maxIndex] = body;
-
-            minBodyEdges.set(minIndex, body.getIndex());
-            maxBodyEdges.set(maxIndex, body.getIndex());
-
-            // preserve ending sentinel value
-            edges[maxIndex + 1] = Integer.MAX_VALUE;
-            edgeIsMax[maxIndex + 1] = true;
-
-            // sort to proper position
-            sortMinDown(minIndex, updateOverlaps);
-            sortMaxDown(maxIndex, updateOverlaps);
-        }
-
-        public void updateEdges(Component<CollisionBody> body, double newMin,
-                                double newMax) {
-            int minIndex = minBodyEdges.get(body.getIndex());
-            int maxIndex = maxBodyEdges.get(body.getIndex());
-
-            int sNewMin = Functions.sortableFloatToIntBits((float) newMin);
-            int sNewMax = Functions.sortableFloatToIntBits((float) newMax);
-
-            // calculate change along axis
-            int dmin = sNewMin - edges[minIndex];
-            int dmax = sNewMax - edges[maxIndex];
-
-            // assign edge values
-            edges[minIndex] = sNewMin;
-            edges[maxIndex] = sNewMax;
-
-            // expand (only adds overlaps)
-            if (dmin < 0) {
-                sortMinDown(minIndex, true);
-            }
-            if (dmax > 0) {
-                sortMaxUp(maxIndex, true);
-            }
-
-            // shrink (only removes overlaps)
-            if (dmin > 0) {
-                sortMinUp(minIndex, true);
-            }
-            if (dmax < 0) {
-                sortMaxDown(maxIndex, true);
-            }
-        }
-
-        public boolean test2DOverlap(int bodyA, int bodyB) {
-            // optimization: check the array index instead of the actual
-            // coordinate value, since we know that they're sorted
-            boolean a1A = axis1.maxBodyEdges.get(bodyA) < axis1.minBodyEdges.get(bodyB);
-            boolean a1B = axis1.maxBodyEdges.get(bodyB) < axis1.minBodyEdges.get(bodyA);
-            boolean a2A = axis2.maxBodyEdges.get(bodyA) < axis2.minBodyEdges.get(bodyB);
-            boolean a2B = axis2.maxBodyEdges.get(bodyB) < axis2.minBodyEdges.get(bodyA);
-
-            return !(a1A || a1B || a2A || a2B);
-        }
-
-        public void sortMinDown(int edge, boolean updateOverlaps) {
-            int prevEdge = edge - 1;