1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.tests.g3d; 18 19 import com.badlogic.gdx.Gdx; 20 import com.badlogic.gdx.graphics.Camera; 21 import com.badlogic.gdx.graphics.Color; 22 import com.badlogic.gdx.graphics.GL20; 23 import com.badlogic.gdx.graphics.PerspectiveCamera; 24 import com.badlogic.gdx.graphics.VertexAttributes.Usage; 25 import com.badlogic.gdx.graphics.g3d.Attribute; 26 import com.badlogic.gdx.graphics.g3d.Material; 27 import com.badlogic.gdx.graphics.g3d.Model; 28 import com.badlogic.gdx.graphics.g3d.ModelBatch; 29 import com.badlogic.gdx.graphics.g3d.ModelInstance; 30 import com.badlogic.gdx.graphics.g3d.Renderable; 31 import com.badlogic.gdx.graphics.g3d.Shader; 32 import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; 33 import com.badlogic.gdx.graphics.g3d.model.Node; 34 import com.badlogic.gdx.graphics.g3d.shaders.BaseShader; 35 import com.badlogic.gdx.graphics.g3d.utils.BaseShaderProvider; 36 import com.badlogic.gdx.graphics.g3d.utils.CameraInputController; 37 import com.badlogic.gdx.graphics.g3d.utils.DefaultShaderProvider; 38 import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder; 39 import com.badlogic.gdx.graphics.g3d.utils.RenderContext; 40 import com.badlogic.gdx.graphics.glutils.ShaderProgram; 41 import com.badlogic.gdx.math.MathUtils; 42 import com.badlogic.gdx.math.Matrix4; 43 import com.badlogic.gdx.tests.utils.GdxTest; 44 import com.badlogic.gdx.utils.Array; 45 import com.badlogic.gdx.utils.GdxRuntimeException; 46 47 public class ShaderTest extends GdxTest { 48 // Create a custom attribute, see https://github.com/libgdx/libgdx/wiki/Material-and-environment 49 // See also: http://blog.xoppa.com/using-materials-with-libgdx/ 50 public static class TestAttribute extends Attribute { 51 public final static String Alias = "Test"; 52 public final static long ID = register(Alias); 53 54 public float value; 55 TestAttribute(final float value)56 protected TestAttribute (final float value) { 57 super(ID); 58 this.value = value; 59 } 60 61 @Override copy()62 public Attribute copy () { 63 return new TestAttribute(value); 64 } 65 66 @Override equals(Attribute other)67 protected boolean equals (Attribute other) { 68 return ((TestAttribute)other).value == value; 69 } 70 71 @Override compareTo(Attribute o)72 public int compareTo (Attribute o) { 73 if (type != o.type) return type < o.type ? -1 : 1; 74 float otherValue = ((TestAttribute)o).value; 75 return MathUtils.isEqual(value, otherValue) ? 0 : (value < otherValue ? -1 : 1); 76 } 77 } 78 79 // Create a custom shader, see also http://blog.xoppa.com/creating-a-shader-with-libgdx 80 // BaseShader adds some basic functionality used to manage uniforms etc. 81 public static class TestShader extends BaseShader { 82 // @off 83 public final static String vertexShader = 84 "attribute vec3 a_position;\n" 85 + "uniform mat4 u_projTrans;\n" 86 + "uniform mat4 u_worldTrans;\n" 87 + "void main() {\n" 88 + " gl_Position = u_projTrans * u_worldTrans * vec4(a_position, 1.0);\n" 89 + "}\n"; 90 91 public final static String fragmentShader = 92 "#ifdef GL_ES\n" 93 + "#define LOWP lowp\n" 94 + "precision mediump float;\n" 95 + "#else\n" 96 + "#define LOWP\n" 97 + "#endif\n" 98 99 + "uniform float u_test;\n" 100 + "#ifdef HasDiffuseColor\n" 101 + "uniform vec4 u_color;\n" 102 + "#endif //HasDiffuseColor\n" 103 104 + "void main() {\n" 105 + "#ifdef HasDiffuseColor\n" 106 + " gl_FragColor.rgb = u_color.rgb * vec3(u_test);\n" 107 + "#else\n" 108 + " gl_FragColor.rgb = vec3(u_test);\n" 109 + "#endif //HasDiffuseColor\n" 110 + "}\n"; 111 // @on 112 113 protected final int u_projTrans = register(new Uniform("u_projTrans")); 114 protected final int u_worldTrans = register(new Uniform("u_worldTrans")); 115 protected final int u_test = register(new Uniform("u_test")); 116 protected final int u_color = register(new Uniform("u_color")); 117 118 protected final ShaderProgram program; 119 private boolean withColor; 120 TestShader(Renderable renderable)121 public TestShader (Renderable renderable) { 122 super(); 123 withColor = renderable.material.has(ColorAttribute.Diffuse); 124 if (withColor) 125 Gdx.app.log("ShaderTest", "Compiling test shader with u_color uniform"); 126 else 127 Gdx.app.log("ShaderTest", "Compiling test shader without u_color uniform"); 128 129 String prefix = withColor ? "#define HasDiffuseColor\n" : ""; 130 program = new ShaderProgram(vertexShader, prefix + fragmentShader); 131 132 if (!program.isCompiled()) throw new GdxRuntimeException("Couldn't compile shader " + program.getLog()); 133 String log = program.getLog(); 134 if (log.length() > 0) Gdx.app.error("ShaderTest", "Shader compilation log: " + log); 135 } 136 137 @Override init()138 public void init () { 139 super.init(program, null); 140 } 141 142 @Override compareTo(Shader other)143 public int compareTo (Shader other) { 144 return 0; 145 } 146 147 @Override canRender(Renderable instance)148 public boolean canRender (Renderable instance) { 149 return instance.material.has(TestAttribute.ID) && (instance.material.has(ColorAttribute.Diffuse) == withColor); 150 } 151 152 @Override begin(Camera camera, RenderContext context)153 public void begin (Camera camera, RenderContext context) { 154 program.begin(); 155 context.setDepthTest(GL20.GL_LEQUAL, 0f, 1f); 156 context.setDepthMask(true); 157 set(u_projTrans, camera.combined); 158 } 159 160 @Override render(Renderable renderable)161 public void render (Renderable renderable) { 162 set(u_worldTrans, renderable.worldTransform); 163 164 TestAttribute testAttr = (TestAttribute)renderable.material.get(TestAttribute.ID); 165 set(u_test, testAttr.value); 166 167 if (withColor) { 168 ColorAttribute colorAttr = (ColorAttribute)renderable.material.get(ColorAttribute.Diffuse); 169 set(u_color, colorAttr.color); 170 } 171 172 renderable.meshPart.render(program); 173 } 174 175 @Override end()176 public void end () { 177 program.end(); 178 } 179 180 @Override dispose()181 public void dispose () { 182 super.dispose(); 183 program.dispose(); 184 } 185 } 186 187 public PerspectiveCamera cam; 188 public CameraInputController camController; 189 public ModelBatch modelBatch; 190 public Model model; 191 public Array<ModelInstance> instances = new Array<ModelInstance>(); 192 public TestAttribute testAttribute1, testAttribute2; 193 194 @Override create()195 public void create () { 196 modelBatch = new ModelBatch(new DefaultShaderProvider() { 197 @Override 198 protected Shader createShader (Renderable renderable) { 199 if (renderable.material.has(TestAttribute.ID)) return new TestShader(renderable); 200 return super.createShader(renderable); 201 } 202 }); 203 204 cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 205 cam.position.set(0f, 0f, 20f); 206 cam.lookAt(0, 0, 0); 207 cam.near = 1f; 208 cam.far = 300f; 209 cam.update(); 210 211 camController = new CameraInputController(cam); 212 Gdx.input.setInputProcessor(camController); 213 214 Material testMaterial1 = new Material("TestMaterial1", new TestAttribute(1f)); 215 Material redMaterial = new Material("RedMaterial", ColorAttribute.createDiffuse(Color.RED)); 216 Material testMaterial2 = new Material("TestMaterial2", new TestAttribute(1f), ColorAttribute.createDiffuse(Color.BLUE)); 217 218 ModelBuilder builder = new ModelBuilder(); 219 Node node; 220 221 builder.begin(); 222 node = builder.node(); 223 node.id = "testCone1"; 224 node.translation.set(-10, 0f, 0f); 225 builder.part("testCone", GL20.GL_TRIANGLES, Usage.Position, testMaterial1).cone(5, 5, 5, 20); 226 227 node = builder.node(); 228 node.id = "redSphere"; 229 builder.part("redSphere", GL20.GL_TRIANGLES, Usage.Position, redMaterial).sphere(5, 5, 5, 20, 20); 230 231 node = builder.node(); 232 node.id = "testCone1"; 233 node.translation.set(10, 0f, 0f); 234 builder.part("testCone", GL20.GL_TRIANGLES, Usage.Position, testMaterial2).cone(5, 5, 5, 20); 235 236 model = builder.end(); 237 238 ModelInstance modelInstance; 239 modelInstance = new ModelInstance(model); 240 testAttribute1 = (TestAttribute)modelInstance.getMaterial("TestMaterial1").get(TestAttribute.ID); 241 testAttribute2 = (TestAttribute)modelInstance.getMaterial("TestMaterial2").get(TestAttribute.ID); 242 instances.add(modelInstance); 243 } 244 245 private float counter; 246 247 @Override render()248 public void render () { 249 counter = (counter + Gdx.graphics.getDeltaTime()) % 2.f; 250 testAttribute1.value = Math.abs(1f - counter); 251 testAttribute2.value = 1f - testAttribute1.value; 252 253 camController.update(); 254 255 Gdx.gl.glViewport(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight()); 256 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 257 258 modelBatch.begin(cam); 259 modelBatch.render(instances); 260 modelBatch.end(); 261 } 262 263 @Override dispose()264 public void dispose () { 265 modelBatch.dispose(); 266 model.dispose(); 267 } 268 } 269