Commits

Michael Ludwig committed f0a0abc

Properly switch between normal and additively blended objects.

Comments (0)

Files changed (5)

ferox-scene/src/main/java/com/ferox/scene/controller/ComputePVSTask.java

 
     @Override
     public Task process(EntitySystem system, Job job) {
-        Profiler.push("compute-pvs-root");
+        Profiler.push("compute-pvs");
 
         if (index != null) {
             for (FrustumResult f : frustums) {
-                Profiler.push("compute-pvs");
-
                 VisibilityCallback query = new VisibilityCallback(system);
                 index.query(f.getFrustum(), query);
 
                 // accessing entity properties
                 query.pvs.sort();
                 job.report(new PVSResult(f.getSource(), f.getFrustum(), query.pvs));
-
-                Profiler.pop();
             }
         }
 

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

         // static tree construction that doesn't depend on entities
         final StateNode root = new StateNode(new CameraState(camera)); // children = (opaque, trans) or fog
         StateNode fogNode = getFogNode(camera); // children = (opaque, trans) if non-null
-
-        // FIXME implement switch support for normal vs additive
-        StateNode transparentNode = new StateNode(TransparentState.normalBlend());
-        StateNode opaqueNode = new StateNode(TransparentState.opaque());
+        StateNode opaqueNode = new StateNode(TransparentState.OPAQUE);
 
         // opaque node must be an earlier child node than transparent so atoms
         // are rendered in the correct order
         if (fogNode == null) {
-            root.setChild(0, opaqueNode);
-            root.setChild(1, transparentNode);
+            root.addChild(opaqueNode);
         } else {
-            root.setChild(0, fogNode);
-            fogNode.setChild(0, opaqueNode);
-            fogNode.setChild(1, transparentNode);
+            root.addChild(fogNode);
+            fogNode.addChild(opaqueNode);
         }
 
         // lit and unlit state nodes for opaque node
-        StateNode litNode = new StateNode(new LightingState(true)); // child = shadowmap state
-        StateNode unlitNode = new StateNode(new LightingState(false)); // child = texture states
+        StateNode litNode = new StateNode(LightingState.LIT); // child = shadowmap state
+        StateNode unlitNode = new StateNode(LightingState.UNLIT); // child = texture states
         StateNode smNode = new StateNode(new ShadowMapState(frame.shadowMap,
                                                             shadowmapTextureUnit)); // children = light groups
 
-        opaqueNode.setChild(0, unlitNode);
-        opaqueNode.setChild(1, litNode);
-        litNode.setChild(0, smNode);
+        opaqueNode.addChild(unlitNode);
+        opaqueNode.addChild(litNode);
+        litNode.addChild(smNode);
 
         // insert light group nodes
         IntProperty groupAssgn = lightGroups.getAssignmentProperty();
 
             // build subtree for lighting within transparent state
             Profiler.push("transparent-tree");
-            StateNode aLitNode = new StateNode(new LightingState(true));
-            StateNode aUnlitNode = new StateNode(new LightingState(false));
-            transparentNode.setChild(0, aUnlitNode);
-            transparentNode.setChild(1, aLitNode);
+            StateNode transparentRoot = new StateNode(NullState.INSTANCE,
+                                                      transparentEntities.size());
+            if (fogNode == null) {
+                root.addChild(transparentRoot);
+            } else {
+                fogNode.addChild(transparentRoot);
+            }
 
-            for (int i = 0; i < lightGroups.getGroupCount(); i++) {
-                // add light group state, but copy from opaque tree, since we
-                // don't need to redo the work (note that we are skipping the
-                // shadow map node for transparent objects).
-                // - also, the expected count is updated to reflect that each
-                //   atom gets its own node
-                aLitNode.setChild(i, new StateNode(smNode.getChild(i).getState(),
-                                                   transparentEntities.size()));
-            }
+            boolean nodeIsAdditive = false;
+            boolean nodeIsLit = false;
+            StateNode lastNode = null;
 
             // insert sorted entities into the transparent node of the tree, but
             // they cannot share nodes so that their depth order is preserved
             for (Entity e : transparentEntities) {
                 e.get(renderable);
                 atom = frame.atoms.get(renderable.getIndex());
-                StateNode firstNode = (atom.blinnPhongVersion >= 0 ? aLitNode.getChild(groupAssgn.get(renderable.getIndex())) : aUnlitNode);
-                addTransparentAtom(e, atom, firstNode, frame, groupAssgn);
+
+                boolean litUpdate = (nodeIsLit ? atom.blinnPhongVersion < 0 : atom.blinnPhongVersion >= 0);
+                boolean blendUpdate = (nodeIsAdditive ? !atom.additiveBlending : atom.additiveBlending);
+                if (lastNode == null || litUpdate || blendUpdate) {
+                    StateNode blendNode = new StateNode(atom.additiveBlending ? TransparentState.ADDITIVE : TransparentState.NORMAL,
+                                                        1);
+                    transparentRoot.addChild(blendNode);
+
+                    if (atom.blinnPhongVersion >= 0) {
+                        StateNode lit = new StateNode(LightingState.LIT, 1);
+                        blendNode.addChild(lit);
+                        StateNode sm = new StateNode(smNode.getChild(groupAssgn.get(renderable.getIndex()))
+                                                           .getState(),
+                                                     1);
+                        lit.addChild(sm);
+
+                        lastNode = sm;
+                    } else {
+                        StateNode unlit = new StateNode(LightingState.UNLIT, 1);
+                        blendNode.addChild(unlit);
+
+                        lastNode = unlit;
+                    }
+
+                    nodeIsLit = atom.blinnPhongVersion >= 0;
+                    nodeIsAdditive = atom.additiveBlending;
+                }
+
+                addTransparentAtom(e, atom, lastNode, frame, groupAssgn);
             }
             Profiler.pop();
         }
                                                        blinnPhong, atom.colorStateIndex);
         }
 
+        atom.additiveBlending = transparent.isEnabled() && transparent.isAdditive();
+
         // record new versions
         atom.renderableVersion = newRenderableVersion;
         atom.blinnPhongVersion = newBlinnPhongVersion;
         int decalTextureVersion = -1;
         int blinnPhongVersion = -1;
         int transparentVersion = -1;
+
+        boolean additiveBlending = false;
     }
 }

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

 import com.ferox.renderer.HardwareAccessLayer;
 
 public class LightingState implements State {
+    public static final LightingState LIT = new LightingState(true);
+    public static final LightingState UNLIT = new LightingState(false);
+
     private final boolean lit;
 
     public LightingState(boolean lit) {

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

+package com.ferox.scene.controller.ffp;
+
+import com.ferox.renderer.HardwareAccessLayer;
+
+public class NullState implements State {
+    public static final NullState INSTANCE = new NullState();
+
+    @Override
+    public void visitNode(StateNode currentNode, AppliedEffects effects,
+                          HardwareAccessLayer access) {
+        currentNode.visitChildren(effects, access);
+    }
+}

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

 import com.ferox.renderer.Renderer.BlendFactor;
 
 public class TransparentState implements State {
+    public static final TransparentState OPAQUE = new TransparentState(BlendFactor.ONE,
+                                                                       BlendFactor.ZERO);
+    public static final TransparentState NORMAL = new TransparentState(BlendFactor.SRC_ALPHA,
+                                                                       BlendFactor.ONE_MINUS_SRC_ALPHA);
+    public static final TransparentState ADDITIVE = new TransparentState(BlendFactor.SRC_ALPHA,
+                                                                         BlendFactor.ONE);
+
     private final BlendFactor src;
     private final BlendFactor dst;
 
         newEffects.pushBlending(access.getCurrentContext().getFixedFunctionRenderer());
         currentNode.visitChildren(newEffects, access);
     }
-
-    public static TransparentState opaque() {
-        return new TransparentState(BlendFactor.ONE, BlendFactor.ZERO);
-    }
-
-    public static TransparentState normalBlend() {
-        return new TransparentState(BlendFactor.SRC_ALPHA,
-                                    BlendFactor.ONE_MINUS_SRC_ALPHA);
-    }
-
-    public static TransparentState additiveBlend() {
-        return new TransparentState(BlendFactor.SRC_ALPHA, BlendFactor.ONE);
-    }
 }