Commits

Michael Ludwig  committed 8238c4a

Update to entreri-1.6.1-SNAPSHOT, improve version handling in FFP render task and greatly cleanup/simplify that code, and implement draw style selection in Renderable.

  • Participants
  • Parent commits cf15052

Comments (0)

Files changed (17)

File ferox-scene/src/main/java/com/ferox/scene/Renderable.java

 import com.lhkbob.entreri.SharedInstance;
 import com.lhkbob.entreri.Unmanaged;
 import com.lhkbob.entreri.property.IntProperty;
+import com.lhkbob.entreri.property.IntProperty.DefaultInt;
 import com.lhkbob.entreri.property.ObjectProperty;
 
 /**
  */
 @Requires(Transform.class)
 public final class Renderable extends ComponentData<Renderable> {
+    private static final DrawStyle[] VALUES = DrawStyle.values();
+
     private ObjectProperty<VertexAttribute> vertices;
     private ObjectProperty<VertexBufferObject> indices;
     private ObjectProperty<PolygonType> polyType;
     private IntProperty indexOffset;
     private IntProperty indexCount;
 
+    @DefaultInt(0 /* SOLID */)
+    private IntProperty frontStyle;
+    @DefaultInt(3 /* NONE */)
+    private IntProperty backStyle;
+
     private AxisAlignedBoxProperty localBounds;
     private AxisAlignedBoxProperty worldBounds;
 
     }
 
     /**
+     * @return The DrawStyle for front facing polygons
+     */
+    public DrawStyle getFrontDrawStyle() {
+        return VALUES[frontStyle.get(getIndex())];
+    }
+
+    /**
+     * @return The DrawStyle for back facing polygons
+     */
+    public DrawStyle getBackDrawStyle() {
+        return VALUES[backStyle.get(getIndex())];
+    }
+
+    /**
+     * Set the front and back draw styles for polygons rendered by this
+     * Renderable.
+     * 
+     * @param front The front style
+     * @param back The back style
+     * @return This component
+     * @throws NullPointerException if front or back are null
+     */
+    public Renderable setDrawStyle(DrawStyle front, DrawStyle back) {
+        if (front == null || back == null) {
+            throw new NullPointerException("Arguments cannot be null");
+        }
+
+        frontStyle.set(front.ordinal(), getIndex());
+        backStyle.set(back.ordinal(), getIndex());
+
+        updateVersion();
+        return this;
+    }
+
+    /**
      * Convenience function to set the indices of this Renderable to null,
      * causing it to use implicit array indices. Calling this method with
      * arguments <tt>first</tt> and <tt>count</tt> is the same as calling

File ferox-scene/src/main/java/com/ferox/scene/Transparent.java

 package com.ferox.scene;
 
 import com.lhkbob.entreri.ComponentData;
+import com.lhkbob.entreri.property.BooleanProperty;
+import com.lhkbob.entreri.property.BooleanProperty.DefaultBoolean;
 import com.lhkbob.entreri.property.DoubleProperty;
 import com.lhkbob.entreri.property.DoubleProperty.DefaultDouble;
 
     @DefaultDouble(0.5)
     private DoubleProperty opacity;
 
+    @DefaultBoolean(false)
+    private BooleanProperty additive;
+
     private Transparent() {}
 
     /**
+     * @return True if the transparent object emits light, such as fire might
+     */
+    public boolean isAdditive() {
+        return additive.get(getIndex());
+    }
+
+    /**
+     * Set whether or not the transparent object emits light, and thus needs to
+     * use additive blending. An example would be the particles of fire, which
+     * is additive, versus particles of smoke that do not emit light.
+     * 
+     * @param additive True if object emits light
+     * @return This component
+     */
+    public Transparent setAdditive(boolean additive) {
+        this.additive.set(additive, getIndex());
+        updateVersion();
+        return this;
+    }
+
+    /**
      * Return the opacity of the Entity. This is a value between 0 and 1,
      * representing the fraction of light that is blocked by the Entity. A value
      * of 1 means the Entity is fully opaque and a value of 0 means the Entity

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

         this.camera = camera;
     }
 
-    public Frustum getFrustum() {
-        return camera;
-    }
-
     @Override
     public void visitNode(StateNode currentNode, AppliedEffects effects,
                           HardwareAccessLayer access) {

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

 import com.ferox.renderer.FixedFunctionRenderer;
 import com.ferox.renderer.HardwareAccessLayer;
 
-public class ColorState implements State {
+public class ColorState implements StaticState {
     public static final Vector4 DEFAULT_DIFFUSE = new Vector4(0.8, 0.8, 0.8, 1.0);
     public static final Vector4 DEFAULT_SPECULAR = new Vector4(0.0, 0.0, 0.0, 1.0);
     public static final Vector4 DEFAULT_EMITTED = new Vector4(0.0, 0.0, 0.0, 1.0);

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

+package com.ferox.scene.controller.ffp;
+
+import com.ferox.renderer.FixedFunctionRenderer;
+import com.ferox.renderer.HardwareAccessLayer;
+import com.ferox.renderer.Renderer.DrawStyle;
+
+public class DrawStyleState implements StaticState {
+    private DrawStyle front;
+    private DrawStyle back;
+
+    public void set(DrawStyle front, DrawStyle back) {
+        this.front = front;
+        this.back = back;
+    }
+
+    @Override
+    public void visitNode(StateNode currentNode, AppliedEffects effects,
+                          HardwareAccessLayer access) {
+        FixedFunctionRenderer r = access.getCurrentContext().getFixedFunctionRenderer();
+
+        r.setDrawStyle(front, back);
+
+        currentNode.visitChildren(effects, access);
+    }
+
+    @Override
+    public int hashCode() {
+        return front.hashCode() ^ back.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof DrawStyleState)) {
+            return false;
+        }
+        DrawStyleState s = (DrawStyleState) o;
+        return s.front.equals(front) && s.back.equals(back);
+    }
+}

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

 
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.Future;
 
 import com.ferox.math.AxisAlignedBox;
+import com.ferox.math.ColorRGB;
 import com.ferox.math.Vector4;
 import com.ferox.math.bounds.Frustum;
 import com.ferox.math.bounds.Frustum.FrustumIntersection;
 import com.ferox.renderer.RenderCapabilities;
 import com.ferox.renderer.Renderer.DrawStyle;
 import com.ferox.renderer.Surface;
+import com.ferox.resource.Texture;
+import com.ferox.resource.VertexAttribute;
 import com.ferox.scene.AmbientLight;
 import com.ferox.scene.AtmosphericFog;
 import com.ferox.scene.BlinnPhongMaterial;
 import com.lhkbob.entreri.Entity;
 import com.lhkbob.entreri.EntitySystem;
 import com.lhkbob.entreri.property.IntProperty;
+import com.lhkbob.entreri.property.ObjectProperty;
 import com.lhkbob.entreri.task.Job;
 import com.lhkbob.entreri.task.ParallelAware;
 import com.lhkbob.entreri.task.Task;
         // static tree construction that doesn't depend on entities
         final StateNode root = new StateNode(new CameraState(camera)); // children = lit, unlit or fog
         StateNode fogNode = getFog(camera);
-        StateNode litNode = new StateNode(frame.litState); // child = shadowmap state
-        StateNode unlitNode = new StateNode(frame.unlitState); // child = texture states
+        StateNode litNode = new StateNode(new LightingState(true)); // child = shadowmap state
+        StateNode unlitNode = new StateNode(new LightingState(false)); // child = texture states
         StateNode smNode = new StateNode(new ShadowMapState(frame.shadowMap,
                                                             shadowmapTextureUnit)); // children = light groups
 
                                                               pointLight,
                                                               ambientLight,
                                                               transform),
-                                          frame.textureState.length));
+                                          frame.drawStates.count()));
         }
 
         IntProperty groupAssgn = lightGroups.getAssignmentProperty();
             e.get(renderable);
             atom = frame.atoms.get(renderable.getIndex());
 
-            StateNode firstNode = (atom.lit ? smNode.getChild(groupAssgn.get(renderable.getIndex())) : unlitNode);
+            StateNode firstNode = (atom.blinnPhongVersion >= 0 ? smNode.getChild(groupAssgn.get(renderable.getIndex())) : unlitNode);
+
+            // draw style state
+            StateNode drawNode = firstNode.getChild(atom.drawStateIndex);
+            if (drawNode == null) {
+                drawNode = new StateNode(frame.drawStates.getState(atom.drawStateIndex),
+                                         frame.textureStates.count());
+                firstNode.setChild(atom.drawStateIndex, drawNode);
+            }
 
             // texture state
-            StateNode texNode = firstNode.getChild(atom.textureStateIndex);
+            StateNode texNode = drawNode.getChild(atom.textureStateIndex);
             if (texNode == null) {
-                texNode = new StateNode(frame.textureState[atom.textureStateIndex],
-                                        frame.geometryState.length);
-                firstNode.setChild(atom.textureStateIndex, texNode);
+                texNode = new StateNode(frame.textureStates.getState(atom.textureStateIndex),
+                                        frame.geometryStates.count());
+                drawNode.setChild(atom.textureStateIndex, texNode);
             }
 
             // geometry state
             StateNode geomNode = texNode.getChild(atom.geometryStateIndex);
             if (geomNode == null) {
-                geomNode = new StateNode(frame.geometryState[atom.geometryStateIndex],
-                                         frame.colorState.length);
+                geomNode = new StateNode(frame.geometryStates.getState(atom.geometryStateIndex),
+                                         frame.colorStates.count());
                 texNode.setChild(atom.geometryStateIndex, geomNode);
             }
 
             // color state
             StateNode colorNode = geomNode.getChild(atom.colorStateIndex);
             if (colorNode == null) {
-                colorNode = new StateNode(frame.colorState[atom.colorStateIndex],
-                                          frame.renderState.length);
+                colorNode = new StateNode(frame.colorStates.getState(atom.colorStateIndex),
+                                          frame.renderStates.count());
                 geomNode.setChild(atom.colorStateIndex, colorNode);
             }
 
             if (renderNode == null) {
                 // must clone the geometry since each node accumulates its own
                 // packed transforms that must be rendered
-                renderNode = new StateNode(frame.renderState[atom.renderStateIndex].cloneGeometry());
+                renderNode = new StateNode(frame.renderStates.getState(atom.renderStateIndex)
+                                                             .cloneGeometry());
                 colorNode.setChild(atom.renderStateIndex, renderNode);
             }
 
     static int count = 0;
 
     private void syncEntityState(Entity e, RenderAtom atom, Frame frame) {
-        // sync render state
-        e.get(renderable);
-        boolean renderableChanged = atom.renderableVersion != renderable.getVersion();
-        if (renderableChanged || atom.renderStateIndex < 0) {
-            atom.renderableVersion = renderable.getVersion();
-            atom.renderStateIndex = frame.updateRenderUsage(atom.renderStateIndex,
-                                                            frame.getRenderState(renderable));
+        // sync render state and draw state
+        int newRenderableVersion = (e.get(renderable) ? renderable.getVersion() : -1);
+        if (newRenderableVersion != atom.renderableVersion || atom.renderStateIndex < 0 || atom.drawStateIndex < 0) {
+            atom.renderStateIndex = frame.getRenderState(renderable,
+                                                         atom.renderStateIndex);
+            atom.drawStateIndex = frame.getDrawStyleState(renderable, atom.drawStateIndex);
         }
 
         // sync geometry state
-        e.get(blinnPhong);
-        boolean blinnChanged = (blinnPhong.isEnabled() ? atom.blinnPhongVersion != blinnPhong.getVersion() : atom.blinnPhongVersion >= 0);
-        if (renderableChanged || blinnChanged || atom.geometryStateIndex < 0) {
-            // renderable version already synced above
-            atom.blinnPhongVersion = (blinnPhong.isEnabled() ? blinnPhong.getVersion() : -1);
-            atom.geometryStateIndex = frame.updateGeometryUsage(atom.geometryStateIndex,
-                                                                frame.getGeometryState(renderable,
-                                                                                       blinnPhong));
+        int newBlinnPhongVersion = (e.get(blinnPhong) ? blinnPhong.getVersion() : -1);
+        if (atom.blinnPhongVersion != newBlinnPhongVersion || atom.renderableVersion != newRenderableVersion || atom.geometryStateIndex < 0) {
+            atom.geometryStateIndex = frame.getGeometryState(renderable, blinnPhong,
+                                                             atom.geometryStateIndex);
         }
 
         // sync texture state
-        e.get(diffuseTexture);
-        e.get(decalTexture);
-        e.get(emittedTexture);
-        boolean dftChanged = (diffuseTexture.isEnabled() ? atom.diffuseTextureVersion != diffuseTexture.getVersion() : atom.diffuseTextureVersion >= 0);
-        boolean dctChanged = (decalTexture.isEnabled() ? atom.decalTextureVersion != decalTexture.getVersion() : atom.decalTextureVersion >= 0);
-        boolean emtChanged = (emittedTexture.isEnabled() ? atom.emittedTextureVersion != emittedTexture.getVersion() : atom.emittedTextureVersion >= 0);
-        if (dftChanged || dctChanged || emtChanged || atom.textureStateIndex < 0) {
-            atom.diffuseTextureVersion = (diffuseTexture.isEnabled() ? diffuseTexture.getVersion() : -1);
-            atom.decalTextureVersion = (decalTexture.isEnabled() ? decalTexture.getVersion() : -1);
-            atom.emittedTextureVersion = (emittedTexture.isEnabled() ? emittedTexture.getVersion() : -1);
-
-            atom.textureStateIndex = frame.updateTextureUsage(atom.textureStateIndex,
-                                                              frame.getTextureState(diffuseTexture,
-                                                                                    decalTexture,
-                                                                                    emittedTexture));
+        int newDiffuseTexVersion = (e.get(diffuseTexture) ? diffuseTexture.getVersion() : -1);
+        int newDecalTexVersion = (e.get(decalTexture) ? decalTexture.getVersion() : -1);
+        int newEmittedTexVersion = (e.get(emittedTexture) ? emittedTexture.getVersion() : -1);
+        if (newDiffuseTexVersion != atom.diffuseTextureVersion || newDecalTexVersion != atom.decalTextureVersion || newEmittedTexVersion != atom.emittedTextureVersion || atom.textureStateIndex < 0) {
+            atom.textureStateIndex = frame.getTextureState(diffuseTexture, decalTexture,
+                                                           emittedTexture,
+                                                           atom.textureStateIndex);
         }
 
         // sync color state
-        e.get(diffuseColor);
-        e.get(specularColor);
-        e.get(emittedColor);
-        e.get(transparent);
-        boolean dfcChanged = (diffuseColor.isEnabled() ? atom.diffuseColorVersion != diffuseColor.getVersion() : atom.diffuseColorVersion >= 0);
-        boolean spcChanged = (specularColor.isEnabled() ? atom.specularColorVersion != specularColor.getVersion() : atom.specularColorVersion >= 0);
-        boolean emcChanged = (emittedColor.isEnabled() ? atom.emittedColorVersion != emittedColor.getVersion() : atom.emittedColorVersion >= 0);
-        boolean transparentChanged = (transparent.isEnabled() ? atom.transparentVersion != transparent.getVersion() : atom.transparentVersion >= 0);
-        if (dfcChanged || spcChanged || emcChanged || blinnChanged || transparentChanged || atom.colorStateIndex < 0) {
-            // blinn phong version already synced
-            atom.diffuseColorVersion = (diffuseColor.isEnabled() ? diffuseColor.getVersion() : -1);
-            atom.specularColorVersion = (specularColor.isEnabled() ? specularColor.getVersion() : -1);
-            atom.emittedColorVersion = (emittedColor.isEnabled() ? emittedColor.getVersion() : -1);
-
-            atom.colorStateIndex = frame.updateColorUsage(atom.colorStateIndex,
-                                                          frame.getColorState(diffuseColor,
-                                                                              specularColor,
-                                                                              emittedColor,
-                                                                              transparent,
-                                                                              blinnPhong));
+        int newTransparentVersion = (e.get(transparent) ? transparent.getVersion() : -1);
+        int newDiffuseColorVersion = (e.get(diffuseColor) ? diffuseColor.getVersion() : -1);
+        int newSpecularColorVersion = (e.get(specularColor) ? specularColor.getVersion() : -1);
+        int newEmittedColorVersion = (e.get(emittedColor) ? emittedColor.getVersion() : -1);
+        if (newTransparentVersion != atom.transparentVersion || newDiffuseColorVersion != atom.diffuseColorVersion || newSpecularColorVersion != atom.specularColorVersion || newEmittedColorVersion != atom.emittedColorVersion || atom.colorStateIndex < 0) {
+            atom.colorStateIndex = frame.getColorState(diffuseColor, specularColor,
+                                                       emittedColor, transparent,
+                                                       blinnPhong, atom.colorStateIndex);
         }
 
-        // lit state
-        atom.lit = blinnPhong.isEnabled();
+        // record new versions
+        atom.renderableVersion = newRenderableVersion;
+        atom.blinnPhongVersion = newBlinnPhongVersion;
+        atom.diffuseTextureVersion = newDiffuseTexVersion;
+        atom.decalTextureVersion = newDecalTexVersion;
+        atom.emittedTextureVersion = newEmittedTexVersion;
+        atom.transparentVersion = newTransparentVersion;
+        atom.diffuseColorVersion = newDiffuseColorVersion;
+        atom.specularColorVersion = newSpecularColorVersion;
+        atom.emittedColorVersion = newEmittedColorVersion;
+    }
+
+    private static class Frame {
+        final ShadowMapCache shadowMap;
+
+        //FIXME        TransparentState[] transparentStates;
+
+        StateCache<TextureState> textureStates;
+        StateCache<GeometryState> geometryStates;
+        StateCache<ColorState> colorStates;
+        StateCache<RenderState> renderStates;
+        StateCache<DrawStyleState> drawStates;
+
+        // per-entity tracking
+        ObjectProperty<RenderAtom> atoms;
+
+        private final int diffuseTextureUnit;
+        private final int emissiveTextureUnit;
+        private final int decalTextureUnit;
+
+        Frame(ShadowMapCache map, int diffuseTextureUnit, int decalTextureUnit,
+              int emissiveTextureUnit) {
+            shadowMap = map;
+
+            this.diffuseTextureUnit = diffuseTextureUnit;
+            this.decalTextureUnit = decalTextureUnit;
+            this.emissiveTextureUnit = emissiveTextureUnit;
+
+            textureStates = new StateCache<TextureState>(TextureState.class);
+            geometryStates = new StateCache<GeometryState>(GeometryState.class);
+            colorStates = new StateCache<ColorState>(ColorState.class);
+            renderStates = new StateCache<RenderState>(RenderState.class);
+            drawStates = new StateCache<DrawStyleState>(DrawStyleState.class);
+        }
+
+        int getTextureState(DiffuseColorMap diffuse, DecalColorMap decal,
+                            EmittedColorMap emitted, int oldIndex) {
+            Texture diffuseTex = null;
+            Texture decalTex = null;
+            Texture emittedTex = null;
+
+            VertexAttribute diffuseCoord = null;
+            VertexAttribute decalCoord = null;
+            VertexAttribute emittedCoord = null;
+
+            if (diffuse.isEnabled()) {
+                diffuseTex = diffuse.getTexture();
+                diffuseCoord = diffuse.getTextureCoordinates();
+            }
+            if (decal.isEnabled()) {
+                decalTex = decal.getTexture();
+                decalCoord = decal.getTextureCoordinates();
+            }
+            if (emitted.isEnabled()) {
+                emittedTex = emitted.getTexture();
+                emittedCoord = emitted.getTextureCoordinates();
+            }
+
+            TextureState state = new TextureState(diffuseTextureUnit,
+                                                  decalTextureUnit,
+                                                  emissiveTextureUnit);
+            state.set(diffuseTex, diffuseCoord, decalTex, decalCoord, emittedTex,
+                      emittedCoord);
+
+            return textureStates.getStateIndex(state, oldIndex);
+        }
+
+        int getGeometryState(Renderable renderable, BlinnPhongMaterial blinnPhong,
+                             int oldIndex) {
+            VertexAttribute verts = renderable.getVertices();
+            VertexAttribute norms = (blinnPhong.isEnabled() ? blinnPhong.getNormals() : null);
+
+            GeometryState state = new GeometryState();
+            state.set(verts, norms);
+
+            return geometryStates.getStateIndex(state, oldIndex);
+        }
+
+        int getColorState(DiffuseColor diffuse, SpecularColor specular,
+                          EmittedColor emitted, Transparent transparent,
+                          BlinnPhongMaterial blinnPhong, int oldIndex) {
+            double alpha = (transparent.isEnabled() ? transparent.getOpacity() : 1.0);
+            double shininess = (blinnPhong.isEnabled() ? blinnPhong.getShininess() : 0.0);
+            ColorRGB d = (diffuse.isEnabled() ? diffuse.getColor() : null);
+            ColorRGB s = (specular.isEnabled() ? specular.getColor() : null);
+            ColorRGB e = (emitted.isEnabled() ? emitted.getColor() : null);
+
+            ColorState state = new ColorState();
+            state.set(d, s, e, alpha, shininess);
+
+            return colorStates.getStateIndex(state, oldIndex);
+        }
+
+        int getRenderState(Renderable renderable, int oldIndex) {
+            // we can assume that the renderable is always valid, since
+            // we're processing renderable entities
+            RenderState state = new RenderState();
+            state.set(renderable.getPolygonType(), renderable.getIndices(),
+                      renderable.getIndexOffset(), renderable.getIndexCount());
+
+            return renderStates.getStateIndex(state, oldIndex);
+        }
+
+        int getDrawStyleState(Renderable renderable, int oldIndex) {
+            // we can assume that the renderable is always valid, since
+            // we're processing renderable entities
+            DrawStyleState state = new DrawStyleState();
+            state.set(renderable.getFrontDrawStyle(), renderable.getBackDrawStyle());
+
+            return drawStates.getStateIndex(state, oldIndex);
+        }
+
+        void resetStates() {
+            textureStates = new StateCache<TextureState>(TextureState.class);
+            geometryStates = new StateCache<GeometryState>(GeometryState.class);
+            colorStates = new StateCache<ColorState>(ColorState.class);
+            renderStates = new StateCache<RenderState>(RenderState.class);
+            drawStates = new StateCache<DrawStyleState>(DrawStyleState.class);
+
+            // clearing the render atoms effectively invalidates all of the
+            // version tracking we do as well
+            Arrays.fill(atoms.getIndexedData(), null);
+        }
+
+        boolean needsReset() {
+            return textureStates.needsReset() || geometryStates.needsReset() || colorStates.needsReset() || renderStates.needsReset() || drawStates.needsReset();
+        }
+
+        @SuppressWarnings("unchecked")
+        void decorate(EntitySystem system) {
+            atoms = system.decorate(Renderable.class, new ObjectProperty.Factory(null));
+        }
+    }
+
+    private static class RenderAtom {
+        // state indices
+        int textureStateIndex = -1; // depends on the 3 texture versions
+        int colorStateIndex = -1; // depends on blinnphong-material and 3 color versions
+        int geometryStateIndex = -1; // depends on renderable, blinnphong-material
+        int renderStateIndex = -1; // depends on indices within renderable
+        int drawStateIndex = -1; // depends on drawstyle of renderable
+
+        // component versions
+        int renderableVersion = -1;
+        int diffuseColorVersion = -1;
+        int emittedColorVersion = -1;
+        int specularColorVersion = -1;
+        int diffuseTextureVersion = -1;
+        int emittedTextureVersion = -1;
+        int decalTextureVersion = -1;
+        int blinnPhongVersion = -1;
+        int transparentVersion = -1;
     }
 }

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

-package com.ferox.scene.controller.ffp;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.ferox.math.ColorRGB;
-import com.ferox.resource.Texture;
-import com.ferox.resource.VertexAttribute;
-import com.ferox.scene.BlinnPhongMaterial;
-import com.ferox.scene.DecalColorMap;
-import com.ferox.scene.DiffuseColor;
-import com.ferox.scene.DiffuseColorMap;
-import com.ferox.scene.EmittedColor;
-import com.ferox.scene.EmittedColorMap;
-import com.ferox.scene.Renderable;
-import com.ferox.scene.SpecularColor;
-import com.ferox.scene.Transparent;
-import com.lhkbob.entreri.EntitySystem;
-import com.lhkbob.entreri.property.ObjectProperty;
-
-class Frame {
-    final ShadowMapCache shadowMap;
-
-    //FIXME        TransparentState[] transparentStates;
-    final LightingState litState;
-    final LightingState unlitState;
-
-    TextureState[] textureState;
-    GeometryState[] geometryState;
-    ColorState[] colorState;
-    RenderState[] renderState;
-
-    // per-entity tracking
-    ObjectProperty<RenderAtom> atoms;
-
-    private final int diffuseTextureUnit;
-    private final int emissiveTextureUnit;
-    private final int decalTextureUnit;
-
-    private Map<TextureState, Integer> textureLookup;
-    private Map<GeometryState, Integer> geometryLookup;
-    private Map<ColorState, Integer> colorLookup;
-    private Map<RenderState, Integer> renderLookup;
-
-    private int[] textureUsage;
-    private int[] geometryUsage;
-    private int[] colorUsage;
-    private int[] renderUsage;
-
-    Frame(ShadowMapCache map, int diffuseTextureUnit, int decalTextureUnit,
-          int emissiveTextureUnit) {
-        shadowMap = map;
-
-        this.diffuseTextureUnit = diffuseTextureUnit;
-        this.decalTextureUnit = decalTextureUnit;
-        this.emissiveTextureUnit = emissiveTextureUnit;
-
-        litState = new LightingState(true);
-        unlitState = new LightingState(false);
-
-        textureState = new TextureState[0];
-        geometryState = new GeometryState[0];
-        colorState = new ColorState[0];
-        renderState = new RenderState[0];
-
-        textureUsage = new int[0];
-        geometryUsage = new int[0];
-        colorUsage = new int[0];
-        renderUsage = new int[0];
-
-        textureLookup = new HashMap<TextureState, Integer>();
-        geometryLookup = new HashMap<GeometryState, Integer>();
-        colorLookup = new HashMap<ColorState, Integer>();
-        renderLookup = new HashMap<RenderState, Integer>();
-    }
-
-    int getTextureState(DiffuseColorMap diffuse, DecalColorMap decal,
-                        EmittedColorMap emitted) {
-        Texture diffuseTex = (diffuse.isEnabled() ? diffuse.getTexture() : null);
-        VertexAttribute diffuseCoord = (diffuse.isEnabled() ? diffuse.getTextureCoordinates() : null);
-        Texture decalTex = (decal.isEnabled() ? decal.getTexture() : null);
-        VertexAttribute decalCoord = (decal.isEnabled() ? decal.getTextureCoordinates() : null);
-        Texture emittedTex = (emitted.isEnabled() ? emitted.getTexture() : null);
-        VertexAttribute emittedCoord = (emitted.isEnabled() ? emitted.getTextureCoordinates() : null);
-
-        TextureState state = new TextureState(diffuseTextureUnit,
-                                              decalTextureUnit,
-                                              emissiveTextureUnit);
-        state.set(diffuseTex, diffuseCoord, decalTex, decalCoord, emittedTex,
-                  emittedCoord);
-
-        Integer index = textureLookup.get(state);
-        if (index == null) {
-            // must create a new state
-            index = textureState.length;
-            textureState = Arrays.copyOf(textureState, textureState.length + 1);
-            textureUsage = Arrays.copyOf(textureUsage, textureUsage.length + 1);
-            textureState[index] = state;
-            textureLookup.put(state, index);
-        }
-
-        return index;
-    }
-
-    int getGeometryState(Renderable renderable, BlinnPhongMaterial blinnPhong) {
-        VertexAttribute verts = renderable.getVertices();
-        VertexAttribute norms = (blinnPhong.isEnabled() ? blinnPhong.getNormals() : null);
-
-        GeometryState state = new GeometryState();
-        state.set(verts, norms);
-
-        Integer index = geometryLookup.get(state);
-        if (index == null) {
-            // needs a new state
-            index = geometryState.length;
-            geometryState = Arrays.copyOf(geometryState, geometryState.length + 1);
-            geometryUsage = Arrays.copyOf(geometryUsage, geometryUsage.length + 1);
-            geometryState[index] = state;
-            geometryLookup.put(state, index);
-        }
-
-        return index;
-    }
-
-    int getColorState(DiffuseColor diffuse, SpecularColor specular, EmittedColor emitted,
-                      Transparent transparent, BlinnPhongMaterial blinnPhong) {
-        double alpha = (transparent.isEnabled() ? transparent.getOpacity() : 1.0);
-        double shininess = (blinnPhong.isEnabled() ? blinnPhong.getShininess() : 0.0);
-        ColorRGB d = (diffuse.isEnabled() ? diffuse.getColor() : null);
-        ColorRGB s = (specular.isEnabled() ? specular.getColor() : null);
-        ColorRGB e = (emitted.isEnabled() ? emitted.getColor() : null);
-
-        ColorState state = new ColorState();
-        state.set(d, s, e, alpha, shininess);
-
-        Integer index = colorLookup.get(state);
-        if (index == null) {
-            // must form a new state
-            index = colorState.length;
-            colorState = Arrays.copyOf(colorState, colorState.length + 1);
-            colorUsage = Arrays.copyOf(colorUsage, colorUsage.length + 1);
-            colorState[index] = state;
-            colorLookup.put(state, index);
-        }
-
-        return index;
-    }
-
-    int getRenderState(Renderable renderable) {
-        // we can assume that the renderable is always valid, since
-        // we're processing renderable entities
-        RenderState state = new RenderState();
-        state.set(renderable.getPolygonType(), renderable.getIndices(),
-                  renderable.getIndexOffset(), renderable.getIndexCount());
-
-        //            System.out.println("getting render state");
-        Integer index = renderLookup.get(state);
-        if (index == null) {
-            //                System.out.println("new render state");
-            // must form a new state
-            index = renderState.length;
-            renderState = Arrays.copyOf(renderState, renderState.length + 1);
-            renderUsage = Arrays.copyOf(renderUsage, renderUsage.length + 1);
-            renderState[index] = state;
-            renderLookup.put(state, index);
-        }
-
-        return index;
-    }
-
-    int updateRenderUsage(int oldIndex, int newIndex) {
-        if (oldIndex >= 0) {
-            renderUsage[oldIndex]--;
-        }
-        renderUsage[newIndex]++;
-        return newIndex;
-    }
-
-    int updateGeometryUsage(int oldIndex, int newIndex) {
-        if (oldIndex >= 0) {
-            geometryUsage[oldIndex]--;
-        }
-        geometryUsage[newIndex]++;
-        return newIndex;
-    }
-
-    int updateTextureUsage(int oldIndex, int newIndex) {
-        if (oldIndex >= 0) {
-            textureUsage[oldIndex]--;
-        }
-        textureUsage[newIndex]++;
-        return newIndex;
-    }
-
-    int updateColorUsage(int oldIndex, int newIndex) {
-        if (oldIndex >= 0) {
-            colorUsage[oldIndex]--;
-        }
-        colorUsage[newIndex]++;
-        return newIndex;
-    }
-
-    void resetStates() {
-        textureState = new TextureState[0];
-        geometryState = new GeometryState[0];
-        colorState = new ColorState[0];
-        renderState = new RenderState[0];
-
-        textureUsage = new int[0];
-        geometryUsage = new int[0];
-        colorUsage = new int[0];
-        renderUsage = new int[0];
-
-        textureLookup = new HashMap<TextureState, Integer>();
-        geometryLookup = new HashMap<GeometryState, Integer>();
-        colorLookup = new HashMap<ColorState, Integer>();
-        renderLookup = new HashMap<RenderState, Integer>();
-
-        // clearing the render atoms effectively invalidates all of the
-        // version tracking we do as well
-        Arrays.fill(atoms.getIndexedData(), null);
-    }
-
-    boolean needsReset() {
-        int empty = 0;
-        int total = textureUsage.length + geometryUsage.length + colorUsage.length + renderUsage.length;
-
-        for (int i = 0; i < textureUsage.length; i++) {
-            if (textureUsage[i] == 0) {
-                empty++;
-            }
-        }
-
-        for (int i = 0; i < geometryUsage.length; i++) {
-            if (geometryUsage[i] == 0) {
-                empty++;
-            }
-        }
-
-        for (int i = 0; i < colorUsage.length; i++) {
-            if (colorUsage[i] == 0) {
-                empty++;
-            }
-        }
-
-        for (int i = 0; i < renderUsage.length; i++) {
-            if (renderUsage[i] == 0) {
-                empty++;
-            }
-        }
-
-        return empty / (double) total > .5;
-    }
-
-    @SuppressWarnings("unchecked")
-    void decorate(EntitySystem system) {
-        atoms = system.decorate(Renderable.class, new ObjectProperty.Factory(null));
-    }
-}

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

 import com.ferox.renderer.HardwareAccessLayer;
 import com.ferox.resource.VertexAttribute;
 
-public class GeometryState implements State {
+public class GeometryState implements StaticState {
     private VertexAttribute vertices;
     private VertexAttribute normals;
 

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

         this.lit = lit;
     }
 
-    public boolean isLightingEnabled() {
-        return lit;
-    }
-
     @Override
     public void visitNode(StateNode currentNode, AppliedEffects effects,
                           HardwareAccessLayer access) {

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

-package com.ferox.scene.controller.ffp;
-
-class RenderAtom {
-    // state indices
-    int textureStateIndex = -1; // depends on the 3 texture versions
-    int colorStateIndex = -1; // depends on blinnphong-material and 3 color versions
-    int geometryStateIndex = -1; // depends on renderable, blinnphong-material
-    int renderStateIndex = -1; // depends on indices within renderable
-    boolean lit = false;
-
-    // component versions
-    int renderableVersion = -1;
-    int diffuseColorVersion = -1;
-    int emittedColorVersion = -1;
-    int specularColorVersion = -1;
-    int diffuseTextureVersion = -1;
-    int emittedTextureVersion = -1;
-    int decalTextureVersion = -1;
-    int blinnPhongVersion = -1;
-    int transparentVersion = -1;
-}

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

 import com.ferox.renderer.Renderer.PolygonType;
 import com.ferox.resource.VertexBufferObject;
 
-public class RenderState implements State {
+public class RenderState implements StaticState {
     private VertexBufferObject indices;
     private int indexOffset;
     private int indexCount;

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

         r = access.getCurrentContext().getFixedFunctionRenderer(); // must re-get renderer, though
         effects.pushBlending(r);
         r.setDepthOffsetsEnabled(false);
+        r.setTexture(shadowMapUnit, null);
     }
 
     private void renderShadowPass(Component<? extends Light<?>> shadowCaster,

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

+package com.ferox.scene.controller.ffp;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+public class StateCache<T extends StaticState> {
+    private T[] states;
+    private int[] usage;
+    private Map<T, Integer> lookup;
+
+    @SuppressWarnings("unchecked")
+    public StateCache(Class<T> type) {
+        states = (T[]) Array.newInstance(type, 0);
+        usage = new int[0];
+        lookup = new HashMap<T, Integer>();
+    }
+
+    public int getStateIndex(T newState, int oldIndex) {
+        Integer index = lookup.get(newState);
+        if (index == null) {
+            // must form a new state
+            index = states.length;
+            states = Arrays.copyOf(states, states.length + 1);
+            usage = Arrays.copyOf(usage, usage.length + 1);
+            states[index] = newState;
+            lookup.put(newState, index);
+        }
+
+        // update usage
+        if (oldIndex >= 0) {
+            usage[oldIndex]--;
+        }
+        usage[index]++;
+        return index;
+    }
+
+    public int count() {
+        return states.length;
+    }
+
+    public T getState(int index) {
+        return states[index];
+    }
+
+    public boolean needsReset() {
+        int empty = 0;
+        for (int i = 0; i < usage.length; i++) {
+            if (usage[i] == 0) {
+                empty++;
+            }
+        }
+        return empty / (double) usage.length > .5;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void reset() {
+        states = (T[]) Array.newInstance(states.getClass().getComponentType(), 0);
+        usage = new int[0];
+        lookup = new HashMap<T, Integer>();
+    }
+}

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

+package com.ferox.scene.controller.ffp;
+
+public interface StaticState extends State {
+    @Override
+    public boolean equals(Object o);
+
+    @Override
+    public int hashCode();
+}

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

 import com.ferox.resource.Texture;
 import com.ferox.resource.VertexAttribute;
 
-public class TextureState implements State {
+public class TextureState implements StaticState {
     private final int diffuseTextureUnit;
     private final int decalTextureUnit;
     private final int emittedTextureUnit;

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

+package com.ferox.scene.controller.ffp;
+
+public class TransparentRenderState {
+
+}
 	</scm>
 
 	<properties>
-		<entreri.version>1.6.0</entreri.version>
+		<entreri.version>1.6.1-SNAPSHOT</entreri.version>
 	</properties>
 
 	<profiles>