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.Application; 20 import com.badlogic.gdx.Gdx; 21 import com.badlogic.gdx.Input; 22 import com.badlogic.gdx.InputMultiplexer; 23 import com.badlogic.gdx.assets.AssetManager; 24 import com.badlogic.gdx.graphics.Camera; 25 import com.badlogic.gdx.graphics.GL20; 26 import com.badlogic.gdx.graphics.GL30; 27 import com.badlogic.gdx.graphics.Mesh; 28 import com.badlogic.gdx.graphics.PerspectiveCamera; 29 import com.badlogic.gdx.graphics.Texture; 30 import com.badlogic.gdx.graphics.VertexAttribute; 31 import com.badlogic.gdx.graphics.VertexAttributes; 32 import com.badlogic.gdx.graphics.g2d.SpriteBatch; 33 import com.badlogic.gdx.graphics.g3d.Attributes; 34 import com.badlogic.gdx.graphics.g3d.Material; 35 import com.badlogic.gdx.graphics.g3d.Model; 36 import com.badlogic.gdx.graphics.g3d.ModelCache; 37 import com.badlogic.gdx.graphics.g3d.ModelInstance; 38 import com.badlogic.gdx.graphics.g3d.Renderable; 39 import com.badlogic.gdx.graphics.g3d.Shader; 40 import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute; 41 import com.badlogic.gdx.graphics.g3d.utils.BaseShaderProvider; 42 import com.badlogic.gdx.graphics.g3d.utils.DefaultRenderableSorter; 43 import com.badlogic.gdx.graphics.g3d.utils.DefaultTextureBinder; 44 import com.badlogic.gdx.graphics.g3d.utils.FirstPersonCameraController; 45 import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder; 46 import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder; 47 import com.badlogic.gdx.graphics.g3d.utils.RenderContext; 48 import com.badlogic.gdx.graphics.g3d.utils.RenderableSorter; 49 import com.badlogic.gdx.graphics.g3d.utils.ShaderProvider; 50 import com.badlogic.gdx.graphics.glutils.GLOnlyTextureData; 51 import com.badlogic.gdx.graphics.glutils.ShaderProgram; 52 import com.badlogic.gdx.math.MathUtils; 53 import com.badlogic.gdx.math.Matrix3; 54 import com.badlogic.gdx.math.Vector3; 55 import com.badlogic.gdx.tests.utils.GdxTest; 56 import com.badlogic.gdx.utils.*; 57 import com.badlogic.gdx.utils.StringBuilder; 58 59 import java.nio.ByteBuffer; 60 import java.nio.ByteOrder; 61 import java.nio.IntBuffer; 62 import java.util.HashMap; 63 import java.util.Map; 64 65 /** MRT test compliant with GLES 3.0, with per pixel lighting and normal and specular mapping. 66 * Thanks to http://www.blendswap.com/blends/view/73922 for the cannon model, licensed under CC-BY-SA 67 * 68 /** @author Tomski */ 69 public class MultipleRenderTargetTest extends GdxTest { 70 71 RenderContext renderContext; 72 MRTFrameBuffer frameBuffer; 73 74 PerspectiveCamera camera; 75 FirstPersonCameraController cameraController; 76 77 ShaderProgram mrtSceneShader; 78 79 SpriteBatch batch; 80 Mesh quad; 81 82 ShaderProvider shaderProvider; 83 RenderableSorter renderableSorter; 84 85 ModelCache modelCache; 86 ModelInstance floorInstance; 87 ModelInstance cannon; 88 Array<Light> lights = new Array<Light>(); 89 Array<Renderable> renderables = new Array<Renderable>(); 90 RenderablePool renerablePool = new RenderablePool(); 91 92 static int DIFFUSE_ATTACHMENT = 0; 93 static int NORMAL_ATTACHMENT = 1; 94 static int POSITION_ATTACHMENT = 2; 95 static int DEPTH_ATTACHMENT = 3; 96 97 98 final int NUM_LIGHTS = 10; 99 100 @Override create()101 public void create () { 102 //use default prepend shader code for batch, some gpu drivers are less forgiving 103 batch = new SpriteBatch(); 104 105 ShaderProgram.pedantic = false;//depth texture not currently sampled 106 107 modelCache = new ModelCache(); 108 109 ShaderProgram.prependVertexCode = Gdx.app.getType().equals(Application.ApplicationType.Desktop) ? "#version 140\n #extension GL_ARB_explicit_attrib_location : enable\n" : "#version 300 es\n"; 110 ShaderProgram.prependFragmentCode = Gdx.app.getType().equals(Application.ApplicationType.Desktop) ? "#version 140\n #extension GL_ARB_explicit_attrib_location : enable\n" : "#version 300 es\n"; 111 112 renderContext = new RenderContext(new DefaultTextureBinder(DefaultTextureBinder.ROUNDROBIN)); 113 shaderProvider = new BaseShaderProvider() { 114 @Override 115 protected Shader createShader (Renderable renderable) { 116 return new MRTShader(renderable); 117 } 118 }; 119 renderableSorter = new DefaultRenderableSorter() { 120 @Override 121 public int compare (Renderable o1, Renderable o2) { 122 return o1.shader.compareTo(o2.shader); 123 } 124 125 }; 126 127 mrtSceneShader = new ShaderProgram(Gdx.files.internal("data/g3d/shaders/mrtscene.vert"), 128 Gdx.files.internal("data/g3d/shaders/mrtscene.frag")); 129 if (!mrtSceneShader.isCompiled()) { 130 System.out.println(mrtSceneShader.getLog()); 131 } 132 133 quad = createFullScreenQuad(); 134 135 camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 136 camera.near = 1f; 137 camera.far = 100f; 138 camera.position.set(3, 5, 10); 139 camera.lookAt(0, 2, 0); 140 camera.up.set(0, 1, 0); 141 camera.update(); 142 cameraController = new FirstPersonCameraController(camera); 143 cameraController.setVelocity(50); 144 Gdx.input.setInputProcessor(cameraController); 145 146 frameBuffer = new MRTFrameBuffer(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), 3); 147 148 AssetManager assetManager = new AssetManager(); 149 assetManager.load("data/g3d/materials/cannon.g3db", Model.class); 150 assetManager.finishLoading(); 151 152 Model scene = assetManager.get("data/g3d/materials/cannon.g3db"); 153 154 cannon = new ModelInstance(scene, "Cannon_LP"); 155 cannon.transform.setToTranslationAndScaling(0, 0, 0, 0.001f, 0.001f, 0.001f); 156 157 ModelBuilder modelBuilder = new ModelBuilder(); 158 159 for (int i = 0; i < NUM_LIGHTS; i++) { 160 modelBuilder.begin(); 161 162 Light light = new Light(); 163 light.color.set(MathUtils.random(1f), MathUtils.random(1f), MathUtils.random(1f)); 164 light.position.set(MathUtils.random(-10f, 10f), MathUtils.random(10f, 15f), MathUtils.random(-10f, 10f)); 165 light.vy = MathUtils.random(10f, 20f); 166 light.vx = MathUtils.random(-10f, 10f); 167 light.vz = MathUtils.random(-10f, 10f); 168 169 MeshPartBuilder meshPartBuilder = modelBuilder.part("light", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.ColorPacked | VertexAttributes.Usage.Normal, new Material()); 170 meshPartBuilder.setColor(light.color.x, light.color.y, light.color.z, 1f); 171 meshPartBuilder.sphere(0.2f, 0.2f, 0.2f, 10, 10); 172 173 light.lightInstance = new ModelInstance(modelBuilder.end()); 174 lights.add(light); 175 } 176 177 modelBuilder.begin(); 178 MeshPartBuilder meshPartBuilder = modelBuilder.part("floor", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.ColorPacked | VertexAttributes.Usage.Normal, new Material()); 179 meshPartBuilder.setColor(0.2f, 0.2f, 0.2f, 1f); 180 meshPartBuilder.box(0, -0.1f, 0f, 20f, 0.1f, 20f); 181 floorInstance = new ModelInstance(modelBuilder.end()); 182 183 Gdx.input.setInputProcessor(new InputMultiplexer(this, cameraController)); 184 } 185 186 @Override keyDown(int keycode)187 public boolean keyDown (int keycode) { 188 if (keycode == Input.Keys.SPACE) { 189 for (Light light : lights) { 190 light.vy = MathUtils.random(10f, 20f); 191 light.vx = MathUtils.random(-10f, 10f); 192 light.vz = MathUtils.random(-10f, 10f); 193 } 194 } 195 return super.keyDown(keycode); 196 } 197 198 float track; 199 200 @Override render()201 public void render () { 202 track += Gdx.graphics.getDeltaTime(); 203 204 Gdx.gl.glClearColor(0f, 0f, 0f, 1f); 205 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 206 207 cameraController.update(Gdx.graphics.getDeltaTime()); 208 209 renderContext.begin(); 210 211 frameBuffer.begin(); 212 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 213 214 renerablePool.flush(); 215 renderables.clear(); 216 217 modelCache.begin(camera); 218 modelCache.add(cannon); 219 modelCache.add(floorInstance); 220 for (Light light : lights) { 221 light.update(Gdx.graphics.getDeltaTime()); 222 modelCache.add(light.lightInstance); 223 } 224 modelCache.end(); 225 modelCache.getRenderables(renderables, renerablePool); 226 227 for (Renderable renderable : renderables) { 228 renderable.shader = shaderProvider.getShader(renderable); 229 } 230 231 renderableSorter.sort(camera, renderables); 232 Shader currentShader = null; 233 for (int i = 0; i < renderables.size; i++) { 234 final Renderable renderable = renderables.get(i); 235 if (currentShader != renderable.shader) { 236 if (currentShader != null) currentShader.end(); 237 currentShader = renderable.shader; 238 currentShader.begin(camera, renderContext); 239 } 240 currentShader.render(renderable); 241 } 242 if (currentShader != null) currentShader.end(); 243 244 frameBuffer.end(); 245 246 mrtSceneShader.begin(); 247 mrtSceneShader.setUniformi("u_diffuseTexture", 248 renderContext.textureBinder.bind(frameBuffer.getColorBufferTexture(DIFFUSE_ATTACHMENT))); 249 mrtSceneShader.setUniformi("u_normalTexture", 250 renderContext.textureBinder.bind(frameBuffer.getColorBufferTexture(NORMAL_ATTACHMENT))); 251 mrtSceneShader.setUniformi("u_positionTexture", 252 renderContext.textureBinder.bind(frameBuffer.getColorBufferTexture(POSITION_ATTACHMENT))); 253 mrtSceneShader.setUniformi("u_depthTexture", renderContext.textureBinder.bind(frameBuffer.getColorBufferTexture(DEPTH_ATTACHMENT))); 254 for (int i = 0; i < lights.size; i++) { 255 Light light = lights.get(i); 256 mrtSceneShader.setUniformf("lights[" + i + "].lightPosition", light.position); 257 mrtSceneShader.setUniformf("lights[" + i + "].lightColor", light.color); 258 } 259 mrtSceneShader.setUniformf("u_viewPos", camera.position); 260 mrtSceneShader.setUniformMatrix("u_inverseProjectionMatrix", camera.invProjectionView); 261 quad.render(mrtSceneShader, GL30.GL_TRIANGLE_FAN); 262 mrtSceneShader.end(); 263 renderContext.end(); 264 265 266 batch.disableBlending(); 267 batch.begin(); 268 batch.draw(frameBuffer.getColorBufferTexture(DIFFUSE_ATTACHMENT), 0, 0, Gdx.graphics.getWidth() / 4f, 269 Gdx.graphics.getHeight() / 4f, 0f, 0f, 1f, 1f); 270 batch.draw(frameBuffer.getColorBufferTexture(NORMAL_ATTACHMENT), Gdx.graphics.getWidth() / 4f, 0, 271 Gdx.graphics.getWidth() / 4f, Gdx.graphics.getHeight() / 4f, 0f, 0f, 1f, 1f); 272 batch.draw(frameBuffer.getColorBufferTexture(POSITION_ATTACHMENT), 2 * Gdx.graphics.getWidth() / 4f, 0, 273 Gdx.graphics.getWidth() / 4f, Gdx.graphics.getHeight() / 4f, 0f, 0f, 1f, 1f); 274 batch.draw(frameBuffer.getColorBufferTexture(DEPTH_ATTACHMENT), 3 * Gdx.graphics.getWidth() / 4f, 0, 275 Gdx.graphics.getWidth() / 4f, Gdx.graphics.getHeight() / 4f, 0f, 0f, 1f, 1f); 276 batch.end(); 277 } 278 279 @Override dispose()280 public void dispose () { 281 frameBuffer.dispose(); 282 batch.dispose(); 283 cannon.model.dispose(); 284 floorInstance.model.dispose(); 285 for (Light light : lights) { 286 light.lightInstance.model.dispose(); 287 } 288 mrtSceneShader.dispose(); 289 quad.dispose(); 290 } 291 createFullScreenQuad()292 public Mesh createFullScreenQuad () { 293 294 float[] verts = new float[20]; 295 int i = 0; 296 297 verts[i++] = -1; 298 verts[i++] = -1; 299 verts[i++] = 0; 300 verts[i++] = 0f; 301 verts[i++] = 0f; 302 303 verts[i++] = 1f; 304 verts[i++] = -1; 305 verts[i++] = 0; 306 verts[i++] = 1f; 307 verts[i++] = 0f; 308 309 verts[i++] = 1f; 310 verts[i++] = 1f; 311 verts[i++] = 0; 312 verts[i++] = 1f; 313 verts[i++] = 1f; 314 315 verts[i++] = -1; 316 verts[i++] = 1f; 317 verts[i++] = 0; 318 verts[i++] = 0f; 319 verts[i++] = 1f; 320 321 Mesh mesh = new Mesh(true, 4, 0, 322 new VertexAttribute(VertexAttributes.Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE), 323 new VertexAttribute(VertexAttributes.Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE + "0")); 324 325 mesh.setVertices(verts); 326 return mesh; 327 } 328 329 static class Light { 330 Vector3 position = new Vector3(); 331 Vector3 color = new Vector3(); 332 ModelInstance lightInstance; 333 334 float vy; 335 float vx; 336 float vz; 337 update(float deltaTime)338 public void update (float deltaTime) { 339 vy += -30f * deltaTime; 340 341 position.y += vy * deltaTime; 342 position.x += vx * deltaTime; 343 position.z += vz * deltaTime; 344 345 if (position.y < 0.1f) { 346 vy *= -0.70f; 347 position.y = 0.1f; 348 } 349 if (position.x < -5) { 350 vx = -vx; 351 position.x = -5; 352 } 353 if (position.x > 5) { 354 vx = -vx; 355 position.x = 5; 356 } 357 if (position.z < -5) { 358 vz = -vz; 359 position.z = -5; 360 } 361 if (position.z > 5) { 362 vz = -vz; 363 position.z = 5; 364 } 365 366 lightInstance.transform.setToTranslation(position); 367 } 368 } 369 370 static class MRTShader implements Shader { 371 372 ShaderProgram shaderProgram; 373 long attributes; 374 375 RenderContext context; 376 377 Matrix3 matrix3 = new Matrix3(); 378 static Attributes tmpAttributes = new Attributes(); 379 MRTShader(Renderable renderable)380 public MRTShader (Renderable renderable) { 381 String prefix = ""; 382 if (renderable.material.has(TextureAttribute.Normal)) { 383 prefix += "#define texturedFlag\n"; 384 } 385 386 String vert = Gdx.files.internal("data/g3d/shaders/mrt.vert").readString(); 387 String frag = Gdx.files.internal("data/g3d/shaders/mrt.frag").readString(); 388 shaderProgram = new ShaderProgram(prefix + vert, prefix + frag); 389 if (!shaderProgram.isCompiled()) { 390 throw new GdxRuntimeException(shaderProgram.getLog()); 391 } 392 renderable.material.set(tmpAttributes); 393 attributes = tmpAttributes.getMask(); 394 } 395 396 @Override init()397 public void init () { 398 } 399 400 @Override compareTo(Shader other)401 public int compareTo (Shader other) { 402 //quick and dirty shader sort 403 if (((MRTShader) other).attributes == attributes) return 0; 404 if ((((MRTShader) other).attributes & TextureAttribute.Normal) == 1) return -1; 405 return 1; 406 407 } 408 409 @Override canRender(Renderable instance)410 public boolean canRender (Renderable instance) { 411 return attributes == instance.material.getMask(); 412 } 413 414 @Override begin(Camera camera, RenderContext context)415 public void begin (Camera camera, RenderContext context) { 416 this.context = context; 417 shaderProgram.begin(); 418 shaderProgram.setUniformMatrix("u_projViewTrans", camera.combined); 419 context.setDepthTest(GL20.GL_LEQUAL); 420 context.setCullFace(GL20.GL_BACK); 421 } 422 423 @Override render(Renderable renderable)424 public void render (Renderable renderable) { 425 Material material = renderable.material; 426 427 TextureAttribute diffuseTexture = (TextureAttribute)material.get(TextureAttribute.Diffuse); 428 TextureAttribute normalTexture = (TextureAttribute)material.get(TextureAttribute.Normal); 429 TextureAttribute specTexture = (TextureAttribute)material.get(TextureAttribute.Specular); 430 431 if (diffuseTexture != null) { 432 shaderProgram.setUniformi("u_diffuseTexture", context.textureBinder.bind(diffuseTexture.textureDescription.texture)); 433 } 434 if (normalTexture != null) { 435 shaderProgram.setUniformi("u_normalTexture", context.textureBinder.bind(normalTexture.textureDescription.texture)); 436 } 437 if (specTexture != null) { 438 shaderProgram.setUniformi("u_specularTexture", context.textureBinder.bind(specTexture.textureDescription.texture)); 439 } 440 441 shaderProgram.setUniformMatrix("u_worldTrans", renderable.worldTransform); 442 shaderProgram.setUniformMatrix("u_normalMatrix", matrix3.set(renderable.worldTransform).inv().transpose()); 443 444 renderable.meshPart.render(shaderProgram); 445 } 446 447 @Override end()448 public void end () { 449 450 } 451 452 @Override dispose()453 public void dispose () { 454 shaderProgram.dispose(); 455 } 456 } 457 458 static class MRTFrameBuffer implements Disposable { 459 460 /** the frame buffers **/ 461 private final static Map<Application, Array<MRTFrameBuffer>> buffers = new HashMap<Application, Array<MRTFrameBuffer>>(); 462 463 /** the color buffer texture **/ 464 private Array<Texture> colorTextures; 465 466 /** the default framebuffer handle, a.k.a screen. */ 467 private static int defaultFramebufferHandle; 468 /** true if we have polled for the default handle already. */ 469 private static boolean defaultFramebufferHandleInitialized = false; 470 471 /** the framebuffer handle **/ 472 private int framebufferHandle; 473 474 /** width **/ 475 private final int width; 476 477 /** height **/ 478 private final int height; 479 MRTFrameBuffer(int width, int height, int numColorAttachments)480 MRTFrameBuffer (int width, int height, int numColorAttachments) { 481 this.width = width; 482 this.height = height; 483 build(); 484 485 addManagedFrameBuffer(Gdx.app, this); 486 } 487 createColorTexture(Texture.TextureFilter min, Texture.TextureFilter mag, int internalformat, int format, int type)488 private Texture createColorTexture (Texture.TextureFilter min, Texture.TextureFilter mag, int internalformat, int format, 489 int type) { 490 GLOnlyTextureData data = new GLOnlyTextureData(width, height, 0, internalformat, format, type); 491 Texture result = new Texture(data); 492 result.setFilter(min, mag); 493 result.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge); 494 return result; 495 } 496 createDepthTexture()497 private Texture createDepthTexture () { 498 GLOnlyTextureData data = new GLOnlyTextureData(width, height, 0, GL30.GL_DEPTH_COMPONENT32F, GL30.GL_DEPTH_COMPONENT, 499 GL30.GL_FLOAT); 500 Texture result = new Texture(data); 501 result.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest); 502 result.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge); 503 return result; 504 } 505 disposeColorTexture(Texture colorTexture)506 private void disposeColorTexture (Texture colorTexture) { 507 colorTexture.dispose(); 508 } 509 build()510 private void build () { 511 GL20 gl = Gdx.gl20; 512 513 // iOS uses a different framebuffer handle! (not necessarily 0) 514 if (!defaultFramebufferHandleInitialized) { 515 defaultFramebufferHandleInitialized = true; 516 if (Gdx.app.getType() == Application.ApplicationType.iOS) { 517 IntBuffer intbuf = ByteBuffer.allocateDirect(16 * Integer.SIZE / 8).order(ByteOrder.nativeOrder()) 518 .asIntBuffer(); 519 gl.glGetIntegerv(GL20.GL_FRAMEBUFFER_BINDING, intbuf); 520 defaultFramebufferHandle = intbuf.get(0); 521 } else { 522 defaultFramebufferHandle = 0; 523 } 524 } 525 526 colorTextures = new Array<Texture>(); 527 528 framebufferHandle = gl.glGenFramebuffer(); 529 gl.glBindFramebuffer(GL20.GL_FRAMEBUFFER, framebufferHandle); 530 531 //rgba 532 Texture diffuse = createColorTexture(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest, GL30.GL_RGBA8, 533 GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE); 534 //rgb 535 Texture normal = createColorTexture(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest, GL30.GL_RGB8, 536 GL30.GL_RGB, GL30.GL_UNSIGNED_BYTE); 537 //rgb 538 Texture position = createColorTexture(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest, GL30.GL_RGB8, 539 GL30.GL_RGB, GL30.GL_UNSIGNED_BYTE); 540 Texture depth = createDepthTexture(); 541 542 colorTextures.add(diffuse); 543 colorTextures.add(normal); 544 colorTextures.add(position); 545 colorTextures.add(depth); 546 547 gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL30.GL_TEXTURE_2D, 548 diffuse.getTextureObjectHandle(), 0); 549 gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT1, GL30.GL_TEXTURE_2D, 550 normal.getTextureObjectHandle(), 0); 551 gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT2, GL30.GL_TEXTURE_2D, 552 position.getTextureObjectHandle(), 0); 553 gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL20.GL_DEPTH_ATTACHMENT, GL20.GL_TEXTURE_2D, 554 depth.getTextureObjectHandle(), 0); 555 556 IntBuffer buffer = BufferUtils.newIntBuffer(3); 557 buffer.put(GL30.GL_COLOR_ATTACHMENT0); 558 buffer.put(GL30.GL_COLOR_ATTACHMENT1); 559 buffer.put(GL30.GL_COLOR_ATTACHMENT2); 560 buffer.position(0); 561 Gdx.gl30.glDrawBuffers(3, buffer); 562 563 gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, 0); 564 gl.glBindTexture(GL20.GL_TEXTURE_2D, 0); 565 566 int result = gl.glCheckFramebufferStatus(GL20.GL_FRAMEBUFFER); 567 568 gl.glBindFramebuffer(GL20.GL_FRAMEBUFFER, defaultFramebufferHandle); 569 570 if (result != GL20.GL_FRAMEBUFFER_COMPLETE) { 571 for (Texture colorTexture : colorTextures) 572 disposeColorTexture(colorTexture); 573 574 gl.glDeleteFramebuffer(framebufferHandle); 575 576 if (result == GL20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) 577 throw new IllegalStateException("frame buffer couldn't be constructed: incomplete attachment"); 578 if (result == GL20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS) 579 throw new IllegalStateException("frame buffer couldn't be constructed: incomplete dimensions"); 580 if (result == GL20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) 581 throw new IllegalStateException("frame buffer couldn't be constructed: missing attachment"); 582 if (result == GL20.GL_FRAMEBUFFER_UNSUPPORTED) 583 throw new IllegalStateException("frame buffer couldn't be constructed: unsupported combination of formats"); 584 throw new IllegalStateException("frame buffer couldn't be constructed: unknown error " + result); 585 } 586 } 587 588 /** Releases all resources associated with the FrameBuffer. */ 589 @Override dispose()590 public void dispose () { 591 GL20 gl = Gdx.gl20; 592 593 for (Texture textureAttachment : colorTextures) { 594 disposeColorTexture(textureAttachment); 595 } 596 597 gl.glDeleteFramebuffer(framebufferHandle); 598 599 if (buffers.get(Gdx.app) != null) 600 buffers.get(Gdx.app).removeValue(this, true); 601 } 602 603 /** Makes the frame buffer current so everything gets drawn to it. */ bind()604 public void bind () { 605 Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, framebufferHandle); 606 } 607 608 /** Unbinds the framebuffer, all drawing will be performed to the normal framebuffer from here on. */ unbind()609 public static void unbind () { 610 Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0); 611 } 612 613 /** Binds the frame buffer and sets the viewport accordingly, so everything gets drawn to it. */ begin()614 public void begin () { 615 bind(); 616 setFrameBufferViewport(); 617 } 618 619 /** Sets viewport to the dimensions of framebuffer. Called by {@link #begin()}. */ setFrameBufferViewport()620 protected void setFrameBufferViewport () { 621 Gdx.gl20.glViewport(0, 0, colorTextures.first().getWidth(), colorTextures.first().getHeight()); 622 } 623 624 /** Unbinds the framebuffer, all drawing will be performed to the normal framebuffer from here on. */ end()625 public void end () { 626 end(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 627 } 628 629 /** Unbinds the framebuffer and sets viewport sizes, all drawing will be performed to the normal framebuffer from here on. 630 * 631 * @param x the x-axis position of the viewport in pixels 632 * @param y the y-asis position of the viewport in pixels 633 * @param width the width of the viewport in pixels 634 * @param height the height of the viewport in pixels */ end(int x, int y, int width, int height)635 public void end (int x, int y, int width, int height) { 636 unbind(); 637 Gdx.gl20.glViewport(x, y, width, height); 638 } 639 getColorBufferTexture(int index)640 public Texture getColorBufferTexture (int index) { 641 return colorTextures.get(index); 642 } 643 644 /** @return the height of the framebuffer in pixels */ getHeight()645 public int getHeight () { 646 return colorTextures.first().getHeight(); 647 } 648 649 /** @return the width of the framebuffer in pixels */ getWidth()650 public int getWidth () { 651 return colorTextures.first().getWidth(); 652 } 653 654 /** @return the depth of the framebuffer in pixels (if applicable) */ getDepth()655 public int getDepth () { 656 return colorTextures.first().getDepth(); 657 } 658 addManagedFrameBuffer(Application app, MRTFrameBuffer frameBuffer)659 private static void addManagedFrameBuffer (Application app, MRTFrameBuffer frameBuffer) { 660 Array<MRTFrameBuffer> managedResources = buffers.get(app); 661 if (managedResources == null) 662 managedResources = new Array<MRTFrameBuffer>(); 663 managedResources.add(frameBuffer); 664 buffers.put(app, managedResources); 665 } 666 getManagedStatus(final StringBuilder builder)667 public static StringBuilder getManagedStatus (final StringBuilder builder) { 668 builder.append("Managed buffers/app: { "); 669 for (Application app : buffers.keySet()) { 670 builder.append(buffers.get(app).size); 671 builder.append(" "); 672 } 673 builder.append("}"); 674 return builder; 675 } 676 getManagedStatus()677 public static String getManagedStatus () { 678 return getManagedStatus(new StringBuilder()).toString(); 679 } 680 } 681 682 protected static class RenderablePool extends Pool<Renderable> { 683 protected Array<Renderable> obtained = new Array<Renderable>(); 684 685 @Override newObject()686 protected Renderable newObject () { 687 return new Renderable(); 688 } 689 690 @Override obtain()691 public Renderable obtain () { 692 Renderable renderable = super.obtain(); 693 renderable.environment = null; 694 renderable.material = null; 695 renderable.meshPart.set("", null, 0, 0, 0); 696 renderable.shader = null; 697 obtained.add(renderable); 698 return renderable; 699 } 700 flush()701 public void flush () { 702 super.freeAll(obtained); 703 obtained.clear(); 704 } 705 } 706 707 } 708