From c1fd43cd641fd7767900f2632c68052f4be74c55 Mon Sep 17 00:00:00 2001 From: gered Date: Mon, 1 Jul 2013 13:49:09 -0400 Subject: [PATCH] add custom CameraGroupStrategy implementation just to have it's shader do a simple alpha-test on fragments --- .../AlphaTestCameraGroupStrategy.java | 220 ++++++++++++++++++ src/com/blarg/gdx/graphics/RenderContext.java | 4 +- 2 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 src/com/blarg/gdx/graphics/AlphaTestCameraGroupStrategy.java diff --git a/src/com/blarg/gdx/graphics/AlphaTestCameraGroupStrategy.java b/src/com/blarg/gdx/graphics/AlphaTestCameraGroupStrategy.java new file mode 100644 index 0000000..ff89540 --- /dev/null +++ b/src/com/blarg/gdx/graphics/AlphaTestCameraGroupStrategy.java @@ -0,0 +1,220 @@ +package com.blarg.gdx.graphics; + +import java.util.Comparator; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Camera; +import com.badlogic.gdx.graphics.GL10; +import com.badlogic.gdx.graphics.g3d.decals.Decal; +import com.badlogic.gdx.graphics.g3d.decals.DecalMaterial; +import com.badlogic.gdx.graphics.g3d.decals.GroupStrategy; +import com.badlogic.gdx.graphics.glutils.ShaderProgram; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Disposable; +import com.badlogic.gdx.utils.ObjectMap; +import com.badlogic.gdx.utils.Pool; + +/** + *

+ * Note:
+ * This is almost exactly the same as {@link com.badlogic.gdx.graphics.g3d.decals.CameraGroupStrategy} except that + * this version requires shader support and it's shader has been slightly modified to perform a simple alpha-test + * in the fragment shader. + *

+ * + *

+ * Minimalistic grouping strategy that splits decals into opaque and transparent ones enabling and disabling blending as needed. + * Opaque decals are rendered first (decal color is ignored in opacity check).
+ * Use this strategy only if the vast majority of your decals are opaque and the few transparent ones are unlikely to overlap. + *

+ *

+ * Can produce invisible artifacts when transparent decals overlap each other. + *

+ *

+ * Needs to be explicitely disposed as it might allocate a ShaderProgram when GLSL 2.0 is used. + *

+ *

+ * States (* = any, EV = entry value - same as value before flush):
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
expectsexits on
glDepthMasktrueEV
GL_DEPTH_TESTenabledEV
glDepthFuncGL_LESS | GL_LEQUALEV
GL_BLENDdisabledEV | disabled
glBlendFunc**
GL_TEXTURE_2D*disabled
+ *

*/ +public class AlphaTestCameraGroupStrategy implements GroupStrategy, Disposable { + private static final int GROUP_OPAQUE = 0; + private static final int GROUP_BLEND = 1; + + Pool> arrayPool = new Pool>(16) { + @Override + protected Array newObject () { + return new Array(); + } + }; + Array> usedArrays = new Array>(); + ObjectMap> materialGroups = new ObjectMap>(); + + Camera camera; + ShaderProgram shader; + private final Comparator cameraSorter; + + public AlphaTestCameraGroupStrategy (final Camera camera) { + this(camera, new Comparator() { + @Override + public int compare (Decal o1, Decal o2) { + float dist1 = camera.position.dst(o1.getPosition()); + float dist2 = camera.position.dst(o2.getPosition()); + return (int)Math.signum(dist2 - dist1); + } + }); + } + + public AlphaTestCameraGroupStrategy(Camera camera, Comparator sorter) { + if (!Gdx.graphics.isGL20Available()) + throw new UnsupportedOperationException("AlphaTestCameraGroupStrategy requires shader support."); + + this.camera = camera; + this.cameraSorter = sorter; + createDefaultShader(); + + } + + public void setCamera (Camera camera) { + this.camera = camera; + } + + public Camera getCamera () { + return camera; + } + + @Override + public int decideGroup (Decal decal) { + return decal.getMaterial().isOpaque() ? GROUP_OPAQUE : GROUP_BLEND; + } + + @Override + public void beforeGroup (int group, Array contents) { + if (group == GROUP_BLEND) { + Gdx.gl.glEnable(GL10.GL_BLEND); + contents.sort(cameraSorter); + } else { + for (int i = 0, n = contents.size; i < n; i++) { + Decal decal = contents.get(i); + Array materialGroup = materialGroups.get(decal.getMaterial()); + if (materialGroup == null) { + materialGroup = arrayPool.obtain(); + materialGroup.clear(); + usedArrays.add(materialGroup); + materialGroups.put(decal.getMaterial(), materialGroup); + } + materialGroup.add(decal); + } + + contents.clear(); + for (Array materialGroup : materialGroups.values()) { + contents.addAll(materialGroup); + } + + materialGroups.clear(); + arrayPool.freeAll(usedArrays); + usedArrays.clear(); + } + } + + @Override + public void afterGroup (int group) { + if (group == GROUP_BLEND) { + Gdx.gl.glDisable(GL10.GL_BLEND); + } + } + + @Override + public void beforeGroups () { + Gdx.gl.glEnable(GL10.GL_DEPTH_TEST); + shader.begin(); + shader.setUniformMatrix("u_projectionViewMatrix", camera.combined); + shader.setUniformi("u_texture", 0); + } + + @Override + public void afterGroups () { + shader.end(); + Gdx.gl.glDisable(GL10.GL_TEXTURE_2D); + Gdx.gl.glDisable(GL10.GL_DEPTH_TEST); + } + + private void createDefaultShader () { + String vertexShader = "attribute vec4 " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" // + + "attribute vec4 " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" // + + "attribute vec2 " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" // + + "uniform mat4 u_projectionViewMatrix;\n" // + + "varying vec4 v_color;\n" // + + "varying vec2 v_texCoords;\n" // + + "\n" // + + "void main()\n" // + + "{\n" // + + " v_color = " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" // + + " v_texCoords = " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" // + + " gl_Position = u_projectionViewMatrix * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" // + + "}\n"; + String fragmentShader = "#ifdef GL_ES\n" // + + "precision mediump float;\n" // + + "#endif\n" // + + "varying vec4 v_color;\n" // + + "varying vec2 v_texCoords;\n" // + + "uniform sampler2D u_texture;\n" // + + "void main()\n"// + + "{\n" // + + " vec4 texColor = texture2D(u_texture, v_texCoords);\n" // + + " if (texColor.a > 0.0)\n" // + + " gl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n" // + + " else\n" // + + " discard;\n" // + + "}"; + + shader = new ShaderProgram(vertexShader, fragmentShader); + if (shader.isCompiled() == false) throw new IllegalArgumentException("couldn't compile shader: " + shader.getLog()); + } + + @Override + public ShaderProgram getGroupShader (int group) { + return shader; + } + + @Override + public void dispose () { + if (shader != null) shader.dispose(); + } +} diff --git a/src/com/blarg/gdx/graphics/RenderContext.java b/src/com/blarg/gdx/graphics/RenderContext.java index 10663b9..b4ec487 100644 --- a/src/com/blarg/gdx/graphics/RenderContext.java +++ b/src/com/blarg/gdx/graphics/RenderContext.java @@ -22,7 +22,7 @@ public class RenderContext implements Disposable { public final ScreenPixelScaler pixelScaler; public final SolidColorTextureCache solidColorTextures; - CameraGroupStrategy cameraGroupStrategy; + AlphaTestCameraGroupStrategy cameraGroupStrategy; Camera perspectiveCamera; OrthographicCamera orthographicCamera; @@ -43,7 +43,7 @@ public class RenderContext implements Disposable { setDefaultPerspectiveCamera(); - cameraGroupStrategy = new CameraGroupStrategy(perspectiveCamera); + cameraGroupStrategy = new AlphaTestCameraGroupStrategy(perspectiveCamera); decalBatch = new DecalBatch(cameraGroupStrategy); billboardSpriteBatch = new BillboardSpriteBatch(); }