• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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