Commits

Michael Ludwig committed 39d2a27

Update effects to track blending, and implement multi-pass blending in light group.

  • Participants
  • Parent commits fd6ab60

Comments (0)

Files changed (4)

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

     // to specify all shadowed lights so that all of them can be disabled.
     private final Component<? extends Light<?>> shadowLight;
 
-    // FIXME should this just be a transparentPhase?
-    private final boolean blendingEnabled;
+    // BlendFunction will always be ADD
+    // FIXME do we need to specify separate factors for RGB and alpha?
     private final BlendFactor destBlend;
     private final BlendFactor sourceBlend;
 
     public AppliedEffects(@Const Matrix4 view) {
         shadowedLightingPhase = false;
         shadowLight = null;
-        blendingEnabled = false;
         destBlend = BlendFactor.ZERO;
         sourceBlend = BlendFactor.ONE;
         viewMatrix = view;
     }
 
+    private AppliedEffects(@Const Matrix4 view, boolean shadowedLighting,
+                           BlendFactor sourceBlend, BlendFactor destBlend,
+                           Component<? extends Light<?>> shadowLight) {
+        this.sourceBlend = sourceBlend;
+        this.destBlend = destBlend;
+        this.shadowLight = shadowLight;
+        viewMatrix = view;
+        shadowedLightingPhase = shadowedLighting;
+    }
+
+    public AppliedEffects setBlending(BlendFactor source, BlendFactor dest) {
+        return new AppliedEffects(viewMatrix, shadowedLightingPhase, source, dest, shadowLight);
+    }
+
     public @Const Matrix4 getViewMatrix() {
         return viewMatrix;
     }
     }
 
     public boolean isBlendingEnabled() {
-        return blendingEnabled;
+        return destBlend != BlendFactor.ZERO;
     }
 
     public BlendFactor getSourceBlendFactor() {

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

 import com.lhkbob.entreri.SimpleController;
 
 public class FixedFunctionRenderController extends SimpleController {
-    private final Framework framework;
-    private final TextureSurface shadowMap;
+    private final Framework           framework;
+    private final TextureSurface      shadowMap;
 
-    private final int shadowmapTextureUnit;
-    private final int diffuseTextureUnit;
-    private final int emissiveTextureUnit;
+    private final int                 shadowmapTextureUnit;
+    private final int                 diffuseTextureUnit;
+    private final int                 emissiveTextureUnit;
+    private final int                 decalTextureUnit;
 
-    private List<PVSResult> pvs;
-    private LightGroupResult lightGroups;
+    private List<PVSResult>           pvs;
+    private LightGroupResult          lightGroups;
 
     private final Queue<Future<Void>> previousFrame;
 
 
         RenderCapabilities caps = framework.getCapabilities();
         if (!caps.hasFixedFunctionRenderer()) {
-            throw new IllegalArgumentException("Framework must support a FixedFunctionRenderer");
+            throw new IllegalArgumentException(
+                                               "Framework must support a FixedFunctionRenderer");
         }
 
         this.framework = framework;
 
         int numTex = caps.getMaxFixedPipelineTextures();
         boolean shadowsRequested = shadowMapSize > 0; // size is positive
-        boolean shadowSupport = (caps.getFboSupport() || caps.getPbufferSupport()) &&
-                numTex > 1 && caps.getDepthTextureSupport();
+        boolean shadowSupport = ((caps.getFboSupport() || caps.getPbufferSupport()) &&
+                                 numTex > 1 && caps.getDepthTextureSupport());
 
-                if (shadowsRequested && shadowSupport) {
-                    // convert size to a power of two
-                    int sz = 1;
-                    while(sz < shadowMapSize) {
-                        sz = sz << 1;
-                    }
-                    // create the shadow map
-                    TextureSurfaceOptions options = new TextureSurfaceOptions().setTarget(Target.T_2D)
-                            .setWidth(sz)
-                            .setHeight(sz)
-                            .setUseDepthTexture(true)
-                            .setColorBufferFormats();
-                    shadowMap = framework.createSurface(options);
+        if (shadowsRequested && shadowSupport) {
+            // convert size to a power of two
+            int sz = 1;
+            while (sz < shadowMapSize) {
+                sz = sz << 1;
+            }
+            // create the shadow map
+            TextureSurfaceOptions options = new TextureSurfaceOptions().setTarget(Target.T_2D)
+                                                                       .setWidth(sz)
+                                                                       .setHeight(sz)
+                                                                       .setUseDepthTexture(true)
+                                                                       .setColorBufferFormats();
+            shadowMap = framework.createSurface(options);
 
-                    // set up the depth comparison
-                    Texture sm = shadowMap.getDepthBuffer();
-                    sm.setFilter(Filter.LINEAR);
-                    sm.setWrapMode(WrapMode.CLAMP);
-                    sm.setDepthCompareEnabled(true);
-                    sm.setDepthComparison(Comparison.LEQUAL);
+            // set up the depth comparison
+            Texture sm = shadowMap.getDepthBuffer();
+            sm.setFilter(Filter.LINEAR);
+            sm.setWrapMode(WrapMode.CLAMP);
+            sm.setDepthCompareEnabled(true);
+            sm.setDepthComparison(Comparison.LEQUAL);
 
-                    // use the 3rd unit if available, or the 2nd if not
-                    shadowmapTextureUnit = (numTex > 2 ? 2 : 1);
-                    // reserve one unit for the shadow map
-                    numTex--;
-                } else {
-                    shadowMap = null;
-                    shadowmapTextureUnit = -1;
-                }
+            // use the 3rd unit if available, or the 2nd if not
+            shadowmapTextureUnit = (numTex > 2 ? 2 : 1);
+            // reserve one unit for the shadow map
+            numTex--;
+        } else {
+            shadowMap = null;
+            shadowmapTextureUnit = -1;
+        }
 
-                // FIXME should this be the responsibility of the TextureGroupFactory
-                // I'm not sure because other factories might also need texture units
-                if (numTex >= 2) {
-                    diffuseTextureUnit = 0;
-                    emissiveTextureUnit = 1;
-                } else {
-                    // multiple passes for textures
-                    diffuseTextureUnit = 0;
-                    emissiveTextureUnit = 0;
-                }
+        // FIXME should this be the responsibility of the TextureGroupFactory
+        // I'm not sure because other factories might also need texture units
+        if (numTex >= 2) {
+            diffuseTextureUnit = 0;
+            emissiveTextureUnit = 1;
+        } else {
+            // multiple passes for textures
+            diffuseTextureUnit = 0;
+            emissiveTextureUnit = 0;
+        }
     }
 
     public static long blocktime = 0L;
+
     @Override
     @SuppressWarnings("unchecked")
     public void process(double dt) {
         // go through all results and render all camera frustums
         List<Future<Void>> thisFrame = new ArrayList<Future<Void>>();
         Camera camera = getEntitySystem().createDataInstance(Camera.ID);
-        for (PVSResult visible: pvs) {
+        for (PVSResult visible : pvs) {
             if (visible.getSource().getTypeId() == Camera.ID) {
                 camera.set((Component<Camera>) visible.getSource());
                 thisFrame.add(render(camera.getSurface(), visible.getFrustum(),
 
         // Block until previous frame is completed to prevent the main thread
         // from getting too ahead of the rendering thread.
-        //  - We do the blocking at the end so that this thread finishes all
-        //    processing before waiting on the rendering thread.
+        // - We do the blocking at the end so that this thread finishes all
+        // processing before waiting on the rendering thread.
         long now = System.nanoTime();
-        while(!previousFrame.isEmpty()) {
+        while (!previousFrame.isEmpty()) {
             Future<Void> f = previousFrame.poll();
             try {
                 f.get();
     }
 
     public static long rendertime = 0L;
-    private Future<Void> render(final Surface surface, final Frustum view, Bag<Entity> pvs) {
-        GeometryGroupFactory geomGroup = new GeometryGroupFactory(getEntitySystem(), view.getViewMatrix());
-        //        TextureGroupFactory textureGroup = new TextureGroupFactory(getEntitySystem(), diffuseTextureUnit, emissiveTextureUnit,
-        //                                                                   geomGroup);
-        MaterialGroupFactory materialGroup = new MaterialGroupFactory(getEntitySystem(), geomGroup);
-        LightGroupFactory lightGroup = new LightGroupFactory(getEntitySystem(), lightGroups,
-                                                             framework.getCapabilities().getMaxActiveLights(),
+
+    private Future<Void>
+            render(final Surface surface, final Frustum view, Bag<Entity> pvs) {
+        GeometryGroupFactory geomGroup = new GeometryGroupFactory(getEntitySystem(),
+                                                                  view.getViewMatrix());
+        TextureGroupFactory textureGroup = new TextureGroupFactory(getEntitySystem(),
+                                                                   diffuseTextureUnit,
+                                                                   emissiveTextureUnit,
+                                                                   geomGroup);
+        MaterialGroupFactory materialGroup = new MaterialGroupFactory(getEntitySystem(),
+                                                                      geomGroup);
+        LightGroupFactory lightGroup = new LightGroupFactory(
+                                                             getEntitySystem(),
+                                                             lightGroups,
+                                                             framework.getCapabilities()
+                                                                      .getMaxActiveLights(),
                                                              materialGroup);
-        LightingGroupFactory lightingGroup = new LightingGroupFactory(materialGroup, lightGroup);
+        LightingGroupFactory lightingGroup = new LightingGroupFactory(materialGroup,
+                                                                      lightGroup);
 
         final StateNode rootNode = new StateNode(lightingGroup.newGroup());
-        for (Entity e: pvs) {
+        for (Entity e : pvs) {
             rootNode.add(e);
         }
 

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

 import com.ferox.math.Vector3;
 import com.ferox.math.Vector4;
 import com.ferox.renderer.FixedFunctionRenderer;
+import com.ferox.renderer.Renderer.BlendFactor;
+import com.ferox.renderer.Renderer.BlendFunction;
 import com.ferox.scene.AmbientLight;
 import com.ferox.scene.DirectionLight;
 import com.ferox.scene.Light;
 
         @Override
         public AppliedEffects applyState(FixedFunctionRenderer r, AppliedEffects effects, int index) {
-            if (index > 0) {
-                // FIXME configure additive blending
-            } // FIXME if index == 0 and doing transparent shadows, also do blending?
-
             int numLights = 0;
 
             if (!effects.isShadowBeingRendered() && ambientColor != null) {
                 }
             }
 
-            // if there aren't any configured lights, no need to render
-            // everything
-            return (numLights > 0 || !effects.isShadowBeingRendered() ? effects : null);
+            if (numLights > 0 || !effects.isShadowBeingRendered()) {
+                // update blending state
+                if (index > 0 && !effects.isShadowBeingRendered()) {
+                    // update blending state, we only do this when accumulating into the previous
+                    // lights that were already rendered. If we're in the shadowing pass, we
+                    // know that only a single light is active so it doesn't matter if the index > 0,
+                    // we don't touch the blending
+                    r.setBlendingEnabled(true);
+                    r.setBlendMode(BlendFunction.ADD, effects.getSourceBlendFactor(), BlendFactor.ONE);
+                    return effects.setBlending(effects.getSourceBlendFactor(), BlendFactor.ONE);
+                } else {
+                    return effects;
+                }
+            } else {
+                // if there aren't any configured lights, no need to render everything
+                return null;
+            }
         }
 
         @Override
         public void unapplyState(FixedFunctionRenderer r, AppliedEffects effects, int index) {
-            if (index > 0) {
-                // FIXME revert changes to blending
+            if (index > 0 && !effects.isShadowBeingRendered()) {
+                r.setBlendingEnabled(effects.isBlendingEnabled());
+                r.setBlendMode(BlendFunction.ADD, effects.getSourceBlendFactor(), effects.getDestinationBlendFactor());
             }
         }
     }

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

 
     private final TextureSet access;
 
-    public TextureGroupFactory(EntitySystem system,
-                               int diffuseUnit,
-                               int decalUnit,
-                               int emissiveUnit,
+    public TextureGroupFactory(EntitySystem system, int diffuseUnit, int decalUnit, int emissiveUnit,
                                StateGroupFactory childFactory) {
         diffuse = system.createDataInstance(DiffuseColorMap.ID);
         decal = system.createDataInstance(DecalColorMap.ID);
             if (node == null) {
                 // haven't seen this texture configuration before
                 int availableUnits = 1 + (decalUnit != diffuseUnit ? 1 : 0)
-                        + (emissiveUnit != decalUnit && emissiveUnit != diffuseUnit ? 1
-                                                                                    : 0);
-                int requiredUnits = (diffuse.isEnabled() ? 1 : 0) + (decal.isEnabled() ? 1
-                                                                                       : 0)
-                                                                                       + (emissive.isEnabled() ? 1 : 0);
+                        + (emissiveUnit != decalUnit && emissiveUnit != diffuseUnit ? 1 : 0);
+                int requiredUnits = (diffuse.isEnabled() ? 1 : 0)
+                        + (decal.isEnabled() ? 1 : 0)
+                        + (emissive.isEnabled() ? 1 : 0);
 
                 TextureSet state;
                 if (availableUnits >= requiredUnits) {
                 } else {
                     // hypothetically we could use multiplicative blending and
                     // multiple texture states to simulate multi-texturing, but
-                    // that's expensive and complicated when combined with
-                    // multiple
+                    // that's expensive and complicated when combined with multiple
                     // light groups and shadow mapping (and it's an unlikely
-                    // situation
-                    // to begin with).
+                    // situation to begin with).
                     if (availableUnits == 2) {
                         // we have 3 textures present and only 2 units, we drop
-                        // the
-                        // texture whose unit is the same
+                        // the texture whose unit is the same
                         if (emissiveUnit == decalUnit || emissiveUnit == diffuseUnit) {
                             // emissive is the spare texture to drop
                             state = new TextureSet(diffuse, decal, null);
                         }
                     } else { // available units == 1
                         // we have 2 or 3 textures and only 1 unit, here we
-                        // prefer
-                        // diffuse > decal > emissive
+                        // prefer diffuse > decal > emissive
                         if (diffuse.isEnabled()) {
                             state = new TextureSet(diffuse, null, null);
                         } else if (decal.isEnabled()) {
         }
 
         @Override
-        public AppliedEffects applyGroupState(FixedFunctionRenderer r,
-                                              AppliedEffects effects) {
+        public AppliedEffects applyGroupState(FixedFunctionRenderer r, AppliedEffects effects) {
             return effects;
         }
 
         }
 
         @Override
-        public AppliedEffects applyState(FixedFunctionRenderer r,
-                                         AppliedEffects effects,
-                                         int state) {
+        public AppliedEffects applyState(FixedFunctionRenderer r, AppliedEffects effects, int state) {
             if (textures.diffuse != null) {
                 r.setTexture(diffuseUnit, textures.diffuse);
                 r.setTextureCoordinates(diffuseUnit, textures.diffuseCoords);
                 // multiplicative blending with vertex color
-                r.setTextureCombineRGB(diffuseUnit,
-                                       CombineFunction.MODULATE,
-                                       CombineSource.CURR_TEX,
-                                       CombineOperand.COLOR,
-                                       CombineSource.PREV_TEX,
-                                       CombineOperand.COLOR,
-                                       CombineSource.CONST_COLOR,
-                                       CombineOperand.COLOR);
-                r.setTextureCombineAlpha(diffuseUnit,
-                                         CombineFunction.MODULATE,
-                                         CombineSource.CURR_TEX,
-                                         CombineOperand.ALPHA,
-                                         CombineSource.PREV_TEX,
-                                         CombineOperand.ALPHA,
-                                         CombineSource.CONST_COLOR,
-                                         CombineOperand.ALPHA);
+                r.setTextureCombineRGB(diffuseUnit, CombineFunction.MODULATE,
+                                       CombineSource.CURR_TEX, CombineOperand.COLOR,
+                                       CombineSource.PREV_TEX, CombineOperand.COLOR,
+                                       CombineSource.CONST_COLOR, CombineOperand.COLOR);
+                r.setTextureCombineAlpha(diffuseUnit, CombineFunction.MODULATE,
+                                         CombineSource.CURR_TEX, CombineOperand.ALPHA,
+                                         CombineSource.PREV_TEX, CombineOperand.ALPHA,
+                                         CombineSource.CONST_COLOR, CombineOperand.ALPHA);
             } else {
                 // disable diffuse texture
                 r.setTexture(diffuseUnit, null);
                 r.setTexture(decalUnit, textures.decal);
                 r.setTextureCoordinates(decalUnit, textures.decalCoords);
                 // alpha blended with previous color based on decal map alpha
-                r.setTextureCombineRGB(decalUnit,
-                                       CombineFunction.INTERPOLATE,
-                                       CombineSource.CURR_TEX,
-                                       CombineOperand.COLOR,
-                                       CombineSource.PREV_TEX,
-                                       CombineOperand.COLOR,
-                                       CombineSource.CURR_TEX,
-                                       CombineOperand.ALPHA);
+                r.setTextureCombineRGB(decalUnit, CombineFunction.INTERPOLATE,
+                                       CombineSource.CURR_TEX, CombineOperand.COLOR,
+                                       CombineSource.PREV_TEX, CombineOperand.COLOR,
+                                       CombineSource.CURR_TEX, CombineOperand.ALPHA);
                 // REPLACE with alpha(PREV_TEX) as arg0 preserves original alpha
-                r.setTextureCombineAlpha(decalUnit,
-                                         CombineFunction.REPLACE,
-                                         CombineSource.PREV_TEX,
-                                         CombineOperand.ALPHA,
-                                         CombineSource.CURR_TEX,
-                                         CombineOperand.ALPHA,
-                                         CombineSource.CONST_COLOR,
-                                         CombineOperand.ALPHA);
+                r.setTextureCombineAlpha(decalUnit, CombineFunction.REPLACE,
+                                         CombineSource.PREV_TEX, CombineOperand.ALPHA,
+                                         CombineSource.CURR_TEX, CombineOperand.ALPHA,
+                                         CombineSource.CONST_COLOR, CombineOperand.ALPHA);
             } else {
                 // disable decal texture but only if we're on a different unit
                 if (diffuseUnit != decalUnit) {
                 r.setTexture(emissiveUnit, textures.emissive);
                 r.setTextureCoordinates(emissiveUnit, textures.emissiveCoords);
                 // emitted light is just added to the color output
-                r.setTextureCombineRGB(emissiveUnit,
-                                       CombineFunction.ADD,
-                                       CombineSource.CURR_TEX,
-                                       CombineOperand.COLOR,
-                                       CombineSource.PREV_TEX,
-                                       CombineOperand.COLOR,
-                                       CombineSource.CONST_COLOR,
-                                       CombineOperand.COLOR);
+                r.setTextureCombineRGB(emissiveUnit, CombineFunction.ADD,
+                                       CombineSource.CURR_TEX, CombineOperand.COLOR,
+                                       CombineSource.PREV_TEX, CombineOperand.COLOR,
+                                       CombineSource.CONST_COLOR, CombineOperand.COLOR);
                 // REPLACE with alpha(PREV_TEX) as arg0 preserves the original
                 // alpha
-                r.setTextureCombineAlpha(emissiveUnit,
-                                         CombineFunction.REPLACE,
-                                         CombineSource.PREV_TEX,
-                                         CombineOperand.ALPHA,
-                                         CombineSource.CURR_TEX,
-                                         CombineOperand.ALPHA,
-                                         CombineSource.CONST_COLOR,
-                                         CombineOperand.ALPHA);
+                r.setTextureCombineAlpha(emissiveUnit, CombineFunction.REPLACE,
+                                         CombineSource.PREV_TEX, CombineOperand.ALPHA,
+                                         CombineSource.CURR_TEX, CombineOperand.ALPHA,
+                                         CombineSource.CONST_COLOR, CombineOperand.ALPHA);
             } else {
                 // disable emissive texture, but only if we're on a
                 // different unit (to prevent overwrite)
         }
 
         @Override
-        public void unapplyState(FixedFunctionRenderer r,
-                                 AppliedEffects effects,
-                                 int state) {
+        public void unapplyState(FixedFunctionRenderer r, AppliedEffects effects, int state) {
             // do nothing
         }
     }
             }
             TextureSet t = (TextureSet) o;
 
-            return (equals(diffuse, diffuseCoords, t.diffuse, t.diffuseCoords) && equals(decal,
-                                                                                         decalCoords,
-                                                                                         t.decal,
-                                                                                         t.decalCoords) && equals(emissive,
-                                                                                                                  emissiveCoords,
-                                                                                                                  t.emissive,
-                                                                                                                  t.emissiveCoords));
+            return (equals(diffuse, diffuseCoords, t.diffuse, t.diffuseCoords) &&
+                    equals(decal, decalCoords, t.decal, t.decalCoords)
+                    && equals(emissive, emissiveCoords, t.emissive, t.emissiveCoords));
         }
 
-        private static boolean equals(Texture t1,
-                                      VertexAttribute tc1,
-                                      Texture t2,
-                                      VertexAttribute tc2) {
+        private static boolean equals(Texture t1, VertexAttribute tc1, Texture t2, VertexAttribute tc2) {
             if (t1 == t2) {
                 // check texture coordinates
                 if (tc1 != tc2) {