Commits

Michael Ludwig committed 4865cee

Implement transparent mesh sorting, consolidate render state API, remove StaticState, remove unnecessary getters from states.

  • Participants
  • Parent commits e35c295

Comments (0)

Files changed (14)

ferox-scene/src/main/java/com/ferox/scene/Material.java

         if (normals == null) {
             throw new NullPointerException("Normals cannot be null");
         }
-        if (normals.getData().getData().getDataType() != DataType.FLOAT) {
+        if (normals.getVBO().getData().getDataType() != DataType.FLOAT) {
             throw new IllegalArgumentException("Normals must have FLOAT data");
         }
         if (normals.getElementSize() != 3) {

ferox-scene/src/main/java/com/ferox/scene/NormalMap.java

      */
     public NormalMap setTangents(VertexAttribute tangents) {
         if (tangents != null) {
-            if (tangents.getData().getData().getDataType() != DataType.FLOAT) {
+            if (tangents.getVBO().getData().getDataType() != DataType.FLOAT) {
                 throw new IllegalArgumentException("Tangents must have FLOAT data");
             }
             if (tangents.getElementSize() != 3) {

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

         if (vertices == null) {
             throw new NullPointerException("Vertices cannot be null");
         }
-        if (vertices.getData().getData().getDataType() != DataType.FLOAT) {
+        if (vertices.getVBO().getData().getDataType() != DataType.FLOAT) {
             throw new IllegalArgumentException("Vertices must have a datatype of FLOAT");
         }
         if (vertices.getElementSize() == 1) {

ferox-scene/src/main/java/com/ferox/scene/TextureMap.java

         if (texCoords == null) {
             throw new NullPointerException("Texture coordinates cannot be null");
         }
-        if (texCoords.getData().getData().getDataType() != DataType.FLOAT) {
+        if (texCoords.getVBO().getData().getDataType() != DataType.FLOAT) {
             throw new IllegalArgumentException("VertexAttribute must have FLOAT data");
         }
 

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 StaticState {
+public class ColorState implements State {
     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);
         this.shininess = shininess;
     }
 
-    public double getShininess() {
-        return shininess;
-    }
-
-    @Const
-    public Vector4 getDiffuse() {
-        return diffuse;
-    }
-
-    @Const
-    public Vector4 getSpecular() {
-        return specular;
-    }
-
-    @Const
-    public Vector4 getEmitted() {
-        return emitted;
-    }
-
-    public double getAlpha() {
-        return diffuse.w;
-    }
-
     @Override
     public void visitNode(StateNode currentNode, AppliedEffects effects,
                           HardwareAccessLayer access) {

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

 import com.ferox.renderer.RenderCapabilities;
 import com.ferox.renderer.Renderer.DrawStyle;
 import com.ferox.renderer.Surface;
+import com.ferox.resource.BufferData;
+import com.ferox.resource.Resource.UpdatePolicy;
 import com.ferox.resource.Texture;
 import com.ferox.resource.VertexAttribute;
+import com.ferox.resource.VertexBufferObject;
+import com.ferox.resource.VertexBufferObject.StorageMode;
 import com.ferox.scene.AmbientLight;
 import com.ferox.scene.AtmosphericFog;
 import com.ferox.scene.BlinnPhongMaterial;
                     nodeIsAdditive = atom.additiveBlending;
                 }
 
-                addTransparentAtom(e, atom, lastNode, frame, groupAssgn);
+                addTransparentAtom(e, atom, renderable.getVertices(), lastNode, frame,
+                                   groupAssgn);
             }
             Profiler.pop();
         }
 
     static int count = 0;
 
-    private void addTransparentAtom(Entity e, RenderAtom atom, StateNode firstNode,
-                                    Frame frame, IntProperty groupAssgn) {
+    private void addTransparentAtom(Entity e, RenderAtom atom, VertexAttribute vertices,
+                                    StateNode firstNode, Frame frame,
+                                    IntProperty groupAssgn) {
         // texture state
         StateNode texNode = new StateNode(frame.textureStates.getState(atom.textureStateIndex),
                                           1);
 
         // transparent render state
         StateNode renderNode = new StateNode(frame.renderStates.getState(atom.renderStateIndex)
-                                                               .cloneTransparent());
+                                                               .newTransparentRenderState(vertices,
+                                                                                          frame.sortedIndices));
         colorNode.setChild(0, renderNode);
 
         // now record transform into the state
             // must clone the geometry since each node accumulates its own
             // packed transforms that must be rendered
             renderNode = new StateNode(frame.renderStates.getState(atom.renderStateIndex)
-                                                         .cloneGeometry());
+                                                         .newOpaqueRenderState());
             colorNode.setChild(atom.renderStateIndex, renderNode);
         }
 
 
     private static class Frame {
         final ShadowMapCache shadowMap;
+        final VertexBufferObject sortedIndices;
 
         StateCache<TextureState> textureStates;
         StateCache<GeometryState> geometryStates;
         StateCache<ColorState> colorStates;
-        StateCache<RenderState> renderStates;
+        StateCache<IndexBufferState> renderStates;
 
         // per-entity tracking
         ObjectProperty<RenderAtom> atoms;
             textureStates = new StateCache<TextureState>(TextureState.class);
             geometryStates = new StateCache<GeometryState>(GeometryState.class);
             colorStates = new StateCache<ColorState>(ColorState.class);
-            renderStates = new StateCache<RenderState>(RenderState.class);
+            renderStates = new StateCache<IndexBufferState>(IndexBufferState.class);
+
+            sortedIndices = new VertexBufferObject(new BufferData(new int[1]),
+                                                   StorageMode.GPU_DYNAMIC);
+            sortedIndices.setUpdatePolicy(UpdatePolicy.MANUAL);
         }
 
         int getTextureState(DiffuseColorMap diffuse, DecalColorMap decal,
         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();
+            IndexBufferState state = new IndexBufferState();
             state.set(renderable.getPolygonType(), renderable.getIndices(),
                       renderable.getIndexOffset(), renderable.getIndexCount());
 
             textureStates = new StateCache<TextureState>(TextureState.class);
             geometryStates = new StateCache<GeometryState>(GeometryState.class);
             colorStates = new StateCache<ColorState>(ColorState.class);
-            renderStates = new StateCache<RenderState>(RenderState.class);
+            renderStates = new StateCache<IndexBufferState>(IndexBufferState.class);
 
             // clearing the render atoms effectively invalidates all of the
             // version tracking we do as well

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

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

ferox-scene/src/main/java/com/ferox/scene/controller/ffp/IndexBufferState.java

+package com.ferox.scene.controller.ffp;
+
+import java.util.Arrays;
+
+import com.ferox.math.Const;
+import com.ferox.math.Functions;
+import com.ferox.math.Matrix4;
+import com.ferox.math.Vector3;
+import com.ferox.renderer.FixedFunctionRenderer;
+import com.ferox.renderer.HardwareAccessLayer;
+import com.ferox.renderer.Renderer.PolygonType;
+import com.ferox.resource.BufferData;
+import com.ferox.resource.VertexAttribute;
+import com.ferox.resource.VertexBufferObject;
+import com.ferox.util.ItemView;
+import com.ferox.util.QuickSort;
+import com.ferox.util.geom.TopologyUtil;
+
+public class IndexBufferState {
+    private VertexBufferObject indices;
+    private int indexOffset;
+    private int indexCount;
+    private PolygonType polyType;
+
+    private final Matrix4 modelMatrix = new Matrix4();
+
+    public void set(PolygonType polyType, VertexBufferObject indices, int offset,
+                    int count) {
+        this.polyType = polyType;
+        this.indices = indices;
+        indexOffset = offset;
+        indexCount = count;
+    }
+
+    public RenderState newOpaqueRenderState() {
+        return new OpaqueRenderState();
+    }
+
+    public RenderState newTransparentRenderState(VertexAttribute vertices,
+                                                 VertexBufferObject sharedIndexBuffer) {
+        return new TransparentRenderState(sharedIndexBuffer, vertices);
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 17;
+        hash = 31 * hash + (indices == null ? 0 : indices.hashCode());
+        hash = 31 * hash + indexOffset;
+        hash = 31 * hash + indexCount;
+        hash = 31 * hash + polyType.hashCode();
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof IndexBufferState)) {
+            return false;
+        }
+        IndexBufferState r = (IndexBufferState) o;
+        return nullEquals(r.indices, indices) && r.indexCount == indexCount && r.indexOffset == indexOffset && r.polyType == polyType;
+    }
+
+    private static boolean nullEquals(Object a, Object b) {
+        return (a == null ? b == null : a.equals(b));
+    }
+
+    private abstract class AbstractRenderState implements RenderState {
+        // packed objects to render
+        protected float[] matrices;
+        protected int count;
+
+        public AbstractRenderState() {
+            count = 0;
+            matrices = new float[16];
+        }
+
+        @Override
+        public void add(@Const Matrix4 transform) {
+            if (count + 16 > matrices.length) {
+                // grow array
+                matrices = Arrays.copyOf(matrices, matrices.length * 2);
+            }
+
+            // use provided matrix
+            transform.get(matrices, count, false);
+
+            count += 16;
+        }
+    }
+
+    private class OpaqueRenderState extends AbstractRenderState {
+        @Override
+        public void visitNode(StateNode currentNode, AppliedEffects effects,
+                              HardwareAccessLayer access) {
+            FixedFunctionRenderer r = access.getCurrentContext()
+                                            .getFixedFunctionRenderer();
+
+            if (indices == null) {
+                for (int i = 0; i < count; i += 16) {
+                    // load and multiply the model with the view
+                    modelMatrix.set(matrices, i, false);
+                    modelMatrix.mul(effects.getViewMatrix(), modelMatrix);
+
+                    r.setModelViewMatrix(modelMatrix);
+                    r.renderArray(polyType, indexOffset, indexCount);
+                }
+            } else {
+                for (int i = 0; i < count; i += 16) {
+                    // load and multiply the model with the view
+                    modelMatrix.set(matrices, i, false);
+                    modelMatrix.mul(effects.getViewMatrix(), modelMatrix);
+
+                    r.setModelViewMatrix(modelMatrix);
+                    r.renderElements(polyType, indices, indexOffset, indexCount);
+                }
+            }
+
+            // restore modelview matrix for lighting, etc.
+            r.setModelViewMatrix(effects.getViewMatrix());
+        }
+    }
+
+    private class TransparentRenderState extends AbstractRenderState {
+        private final VertexBufferObject sortedIndicesShared;
+        private final VertexAttribute vertices;
+
+        public TransparentRenderState(VertexBufferObject sortedIndicesShared,
+                                      VertexAttribute vertices) {
+            this.sortedIndicesShared = sortedIndicesShared;
+            this.vertices = vertices;
+        }
+
+        @Override
+        public void visitNode(StateNode currentNode, AppliedEffects effects,
+                              HardwareAccessLayer access) {
+            FixedFunctionRenderer r = access.getCurrentContext()
+                                            .getFixedFunctionRenderer();
+
+            int inflatedIndexCount = polyType.getPolygonCount(indexCount) * polyType.getPolygonSize();
+            if (sortedIndicesShared.getData().getLength() < inflatedIndexCount) {
+                sortedIndicesShared.setData(new BufferData(new int[inflatedIndexCount]));
+            }
+
+            if (indices == null) {
+                switch (polyType) {
+                case QUAD_STRIP:
+                    TopologyUtil.inflateQuadStripArray(indexOffset, indexCount,
+                                                       sortedIndicesShared, 0);
+                    break;
+                case TRIANGLE_STRIP:
+                    TopologyUtil.inflateTriangleStripArray(indexOffset, indexCount,
+                                                           sortedIndicesShared, 0);
+                    break;
+                default:
+                    TopologyUtil.inflateSimpleArray(indexOffset, indexCount,
+                                                    sortedIndicesShared, 0);
+                    break;
+                }
+            } else {
+                switch (polyType) {
+                case QUAD_STRIP:
+                    TopologyUtil.inflateQuadStrip(indices, indexOffset, indexCount,
+                                                  sortedIndicesShared, 0);
+                    break;
+                case TRIANGLE_STRIP:
+                    TopologyUtil.inflateTriangleStrip(indices, indexOffset, indexCount,
+                                                      sortedIndicesShared, 0);
+                    break;
+                default:
+                    System.arraycopy(indices.getData().getArray(), indexOffset,
+                                     sortedIndicesShared.getData().getArray(), 0,
+                                     indexCount);
+                    break;
+                }
+            }
+
+            FaceView view = new FaceView(polyType,
+                                         sortedIndicesShared,
+                                         indexCount,
+                                         vertices,
+                                         modelMatrix);
+            for (int i = 0; i < count; i += 16) {
+                // load and multiply the model with the view
+                modelMatrix.set(matrices, i, false);
+                modelMatrix.mul(effects.getViewMatrix(), modelMatrix);
+
+                // sort indices within sortedIndicesShared
+                QuickSort.sort(view);
+                sortedIndicesShared.markDirty(0, inflatedIndexCount);
+                access.update(sortedIndicesShared);
+
+                r.setModelViewMatrix(modelMatrix);
+                r.renderElements(polyType, sortedIndicesShared, 0, inflatedIndexCount);
+            }
+
+            // restore modelview matrix for lighting, etc.
+            r.setModelViewMatrix(effects.getViewMatrix());
+        }
+    }
+
+    private static final class FaceView implements ItemView {
+        private final VertexBufferObject indices;
+        private final int count;
+        private final PolygonType polyType;
+
+        private final VertexAttribute vertices;
+        private final Matrix4 modelview;
+
+        private final Vector3 v;
+
+        // this expects the converted index buffer, so it is offset from 0
+        // and contains non-strip polygons only (points, lines, tris, or quads)
+        public FaceView(PolygonType type, VertexBufferObject indices, int count,
+                        VertexAttribute vertices, @Const Matrix4 modelview) {
+            this.indices = indices;
+            this.count = count;
+            this.polyType = type;
+            this.vertices = vertices;
+            this.modelview = modelview;
+            v = new Vector3();
+        }
+
+        @Override
+        public int hash(int index) {
+            int[] iData = indices.getData().getArray();
+            float[] vData = vertices.getVBO().getData().getArray();
+
+            // convert polygon index to vertex index
+            index *= polyType.getPolygonSize();
+
+            float centroid = 0f;
+            for (int i = 0; i < polyType.getPolygonSize(); i++) {
+                v.set(vData, vertices.getArrayIndex(iData[index + i], 0));
+                v.transform(modelview);
+                centroid += v.z;
+            }
+            centroid /= polyType.getPolygonSize();
+
+            // centroid now contains average camera-space z for the polygon 
+            return Functions.sortableFloatToIntBits(centroid);
+        }
+
+        @Override
+        public void swap(int srcIndex, int dstIndex) {
+            int[] data = indices.getData().getArray();
+
+            // convert polygon index to vertex index
+            srcIndex *= polyType.getPolygonSize();
+            dstIndex *= polyType.getPolygonSize();
+
+            // swap the entire polygon
+            for (int i = 0; i < polyType.getPolygonSize(); i++) {
+                int t = data[srcIndex + i];
+                data[srcIndex + i] = data[dstIndex + i];
+                data[dstIndex + i] = t;
+            }
+        }
+
+        @Override
+        public int length() {
+            return polyType.getPolygonCount(count);
+        }
+    }
+}

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

-package com.ferox.scene.controller.ffp;
-
-import java.util.Arrays;
-
-import com.ferox.math.Const;
-import com.ferox.math.Matrix4;
-import com.ferox.renderer.FixedFunctionRenderer;
-import com.ferox.renderer.HardwareAccessLayer;
-import com.ferox.renderer.Renderer.PolygonType;
-import com.ferox.resource.VertexBufferObject;
-
-public class RenderState implements StaticState {
-    protected VertexBufferObject indices;
-    protected int indexOffset;
-    protected int indexCount;
-    protected PolygonType polyType;
-
-    protected final Matrix4 modelMatrix = new Matrix4();
-
-    // packed objects to render
-    protected float[] matrices;
-    protected int count;
-
-    public RenderState() {
-        matrices = new float[16];
-        count = 0;
-    }
-
-    public void set(PolygonType polyType, VertexBufferObject indices, int offset,
-                    int count) {
-        this.polyType = polyType;
-        this.indices = indices;
-        indexOffset = offset;
-        indexCount = count;
-    }
-
-    public PolygonType getPolygonType() {
-        return polyType;
-    }
-
-    public VertexBufferObject getIndices() {
-        return indices;
-    }
-
-    public int getIndexCount() {
-        return indexCount;
-    }
-
-    public int getIndexOffset() {
-        return indexOffset;
-    }
-
-    public void add(@Const Matrix4 transform) {
-        if (count + 16 > matrices.length) {
-            // grow array
-            matrices = Arrays.copyOf(matrices, matrices.length * 2);
-        }
-
-        // use provided matrix
-        transform.get(matrices, count, false);
-
-        count += 16;
-    }
-
-    public void clear() {
-        count = 0;
-    }
-
-    public RenderState cloneGeometry() {
-        RenderState r = new RenderState();
-        r.set(polyType, indices, indexOffset, indexCount);
-        return r;
-    }
-
-    public TransparentRenderState cloneTransparent() {
-        TransparentRenderState r = new TransparentRenderState();
-        r.set(polyType, indices, indexOffset, indexCount);
-        return r;
-    }
-
-    @Override
-    public void visitNode(StateNode currentNode, AppliedEffects effects,
-                          HardwareAccessLayer access) {
-        FixedFunctionRenderer r = access.getCurrentContext().getFixedFunctionRenderer();
-
-        if (indices == null) {
-            for (int i = 0; i < count; i += 16) {
-                // load and multiply the model with the view
-                modelMatrix.set(matrices, i, false);
-                modelMatrix.mul(effects.getViewMatrix(), modelMatrix);
-
-                r.setModelViewMatrix(modelMatrix);
-                r.render(polyType, indexOffset, indexCount);
-            }
-        } else {
-            for (int i = 0; i < count; i += 16) {
-                // load and multiply the model with the view
-                modelMatrix.set(matrices, i, false);
-                modelMatrix.mul(effects.getViewMatrix(), modelMatrix);
-
-                r.setModelViewMatrix(modelMatrix);
-                r.render(polyType, indices, indexOffset, indexCount);
-            }
-        }
-
-        // restore modelview matrix for lighting, etc.
-        r.setModelViewMatrix(effects.getViewMatrix());
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 17;
-        hash = 31 * hash + (indices == null ? 0 : indices.hashCode());
-        hash = 31 * hash + indexOffset;
-        hash = 31 * hash + indexCount;
-        hash = 31 * hash + polyType.hashCode();
-        return hash;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (!(o instanceof RenderState)) {
-            return false;
-        }
-        RenderState r = (RenderState) o;
-        return nullEquals(r.indices, indices) && r.indexCount == indexCount && r.indexOffset == indexOffset && r.polyType == polyType;
-    }
-
-    private static boolean nullEquals(Object a, Object b) {
-        return (a == null ? b == null : a.equals(b));
-    }
-}

ferox-scene/src/main/java/com/ferox/scene/controller/ffp/ShadowMapCache.java

         Transform transform = system.createDataInstance(Transform.class);
 
         GeometryState geom = new GeometryState();
-        RenderState render = new RenderState();
+        IndexBufferState render = new IndexBufferState();
 
         // build up required states and tree simultaneously
         StateNode root = new StateNode(new CameraState(pvs.getFrustum()));
 
         List<GeometryState> geomLookup = new ArrayList<GeometryState>();
-        List<RenderState> renderLookup = new ArrayList<RenderState>();
+        List<IndexBufferState> renderLookup = new ArrayList<IndexBufferState>();
 
         Map<GeometryState, Integer> geomState = new HashMap<GeometryState, Integer>();
-        Map<RenderState, Integer> renderState = new HashMap<RenderState, Integer>();
+        Map<IndexBufferState, Integer> renderState = new HashMap<IndexBufferState, Integer>();
         for (Entity e : pvs.getPotentiallyVisibleSet()) {
             e.get(renderable);
             e.get(transform);
                 renderLookup.add(render);
                 renderState.put(render, geomStateIndex);
                 // create a new state so we don't mutate value stached in collection
-                render = new RenderState();
+                render = new IndexBufferState();
             }
 
             StateNode geomNode = root.getChild(geomStateIndex);
             StateNode renderNode = geomNode.getChild(renderStateIndex);
             if (renderNode == null) {
                 renderNode = new StateNode(renderLookup.get(renderStateIndex)
-                                                       .cloneGeometry());
+                                                       .newOpaqueRenderState());
                 geomNode.setChild(renderStateIndex, renderNode);
             }
 

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

 import java.util.HashMap;
 import java.util.Map;
 
-public class StateCache<T extends StaticState> {
+public class StateCache<T> {
     private T[] states;
     private int[] usage;
     private Map<T, Integer> lookup;

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();
-}

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 StaticState {
+public class TextureState implements State {
     private final int diffuseTextureUnit;
     private final int decalTextureUnit;
     private final int emittedTextureUnit;
         this.emittedTexCoords = emittedTexCoords;
     }
 
-    public Texture getDiffuseTexture() {
-        return diffuseTexture;
-    }
-
-    public Texture getDecalTexture() {
-        return decalTexture;
-    }
-
-    public Texture getEmittedTexture() {
-        return emittedTexture;
-    }
-
-    public VertexAttribute getDiffuseTexCoords() {
-        return diffuseTexCoords;
-    }
-
-    public VertexAttribute getDecalTexCoords() {
-        return decalTexCoords;
-    }
-
-    public VertexAttribute getEmittedTexCoords() {
-        return emittedTexCoords;
-    }
-
     @Override
     public void visitNode(StateNode currentNode, AppliedEffects effects,
                           HardwareAccessLayer access) {

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

-package com.ferox.scene.controller.ffp;
-
-public class TransparentRenderState extends RenderState {
-
-}