/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.tests.g3d;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g3d.Attributes;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelCache;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.Renderable;
import com.badlogic.gdx.graphics.g3d.Shader;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.graphics.g3d.utils.BaseShaderProvider;
import com.badlogic.gdx.graphics.g3d.utils.DefaultRenderableSorter;
import com.badlogic.gdx.graphics.g3d.utils.DefaultTextureBinder;
import com.badlogic.gdx.graphics.g3d.utils.FirstPersonCameraController;
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.graphics.g3d.utils.RenderContext;
import com.badlogic.gdx.graphics.g3d.utils.RenderableSorter;
import com.badlogic.gdx.graphics.g3d.utils.ShaderProvider;
import com.badlogic.gdx.graphics.glutils.GLOnlyTextureData;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix3;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.tests.utils.GdxTest;
import com.badlogic.gdx.utils.*;
import com.badlogic.gdx.utils.StringBuilder;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
/** MRT test compliant with GLES 3.0, with per pixel lighting and normal and specular mapping.
* Thanks to http://www.blendswap.com/blends/view/73922 for the cannon model, licensed under CC-BY-SA
*
/** @author Tomski */
public class MultipleRenderTargetTest extends GdxTest {
RenderContext renderContext;
MRTFrameBuffer frameBuffer;
PerspectiveCamera camera;
FirstPersonCameraController cameraController;
ShaderProgram mrtSceneShader;
SpriteBatch batch;
Mesh quad;
ShaderProvider shaderProvider;
RenderableSorter renderableSorter;
ModelCache modelCache;
ModelInstance floorInstance;
ModelInstance cannon;
Array<Light> lights = new Array<Light>();
Array<Renderable> renderables = new Array<Renderable>();
RenderablePool renerablePool = new RenderablePool();
static int DIFFUSE_ATTACHMENT = 0;
static int NORMAL_ATTACHMENT = 1;
static int POSITION_ATTACHMENT = 2;
static int DEPTH_ATTACHMENT = 3;
final int NUM_LIGHTS = 10;
@Override
public void create () {
//use default prepend shader code for batch, some gpu drivers are less forgiving
batch = new SpriteBatch();
ShaderProgram.pedantic = false;//depth texture not currently sampled
modelCache = new ModelCache();
ShaderProgram.prependVertexCode = Gdx.app.getType().equals(Application.ApplicationType.Desktop) ? "#version 140\n #extension GL_ARB_explicit_attrib_location : enable\n" : "#version 300 es\n";
ShaderProgram.prependFragmentCode = Gdx.app.getType().equals(Application.ApplicationType.Desktop) ? "#version 140\n #extension GL_ARB_explicit_attrib_location : enable\n" : "#version 300 es\n";
renderContext = new RenderContext(new DefaultTextureBinder(DefaultTextureBinder.ROUNDROBIN));
shaderProvider = new BaseShaderProvider() {
@Override
protected Shader createShader (Renderable renderable) {
return new MRTShader(renderable);
}
};
renderableSorter = new DefaultRenderableSorter() {
@Override
public int compare (Renderable o1, Renderable o2) {
return o1.shader.compareTo(o2.shader);
}
};
mrtSceneShader = new ShaderProgram(Gdx.files.internal("data/g3d/shaders/mrtscene.vert"),
Gdx.files.internal("data/g3d/shaders/mrtscene.frag"));
if (!mrtSceneShader.isCompiled()) {
System.out.println(mrtSceneShader.getLog());
}
quad = createFullScreenQuad();
camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.near = 1f;
camera.far = 100f;
camera.position.set(3, 5, 10);
camera.lookAt(0, 2, 0);
camera.up.set(0, 1, 0);
camera.update();
cameraController = new FirstPersonCameraController(camera);
cameraController.setVelocity(50);
Gdx.input.setInputProcessor(cameraController);
frameBuffer = new MRTFrameBuffer(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), 3);
AssetManager assetManager = new AssetManager();
assetManager.load("data/g3d/materials/cannon.g3db", Model.class);
assetManager.finishLoading();
Model scene = assetManager.get("data/g3d/materials/cannon.g3db");
cannon = new ModelInstance(scene, "Cannon_LP");
cannon.transform.setToTranslationAndScaling(0, 0, 0, 0.001f, 0.001f, 0.001f);
ModelBuilder modelBuilder = new ModelBuilder();
for (int i = 0; i < NUM_LIGHTS; i++) {
modelBuilder.begin();
Light light = new Light();
light.color.set(MathUtils.random(1f), MathUtils.random(1f), MathUtils.random(1f));
light.position.set(MathUtils.random(-10f, 10f), MathUtils.random(10f, 15f), MathUtils.random(-10f, 10f));
light.vy = MathUtils.random(10f, 20f);
light.vx = MathUtils.random(-10f, 10f);
light.vz = MathUtils.random(-10f, 10f);
MeshPartBuilder meshPartBuilder = modelBuilder.part("light", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.ColorPacked | VertexAttributes.Usage.Normal, new Material());
meshPartBuilder.setColor(light.color.x, light.color.y, light.color.z, 1f);
meshPartBuilder.sphere(0.2f, 0.2f, 0.2f, 10, 10);
light.lightInstance = new ModelInstance(modelBuilder.end());
lights.add(light);
}
modelBuilder.begin();
MeshPartBuilder meshPartBuilder = modelBuilder.part("floor", GL20.GL_TRIANGLES, VertexAttributes.Usage.Position | VertexAttributes.Usage.ColorPacked | VertexAttributes.Usage.Normal, new Material());
meshPartBuilder.setColor(0.2f, 0.2f, 0.2f, 1f);
meshPartBuilder.box(0, -0.1f, 0f, 20f, 0.1f, 20f);
floorInstance = new ModelInstance(modelBuilder.end());
Gdx.input.setInputProcessor(new InputMultiplexer(this, cameraController));
}
@Override
public boolean keyDown (int keycode) {
if (keycode == Input.Keys.SPACE) {
for (Light light : lights) {
light.vy = MathUtils.random(10f, 20f);
light.vx = MathUtils.random(-10f, 10f);
light.vz = MathUtils.random(-10f, 10f);
}
}
return super.keyDown(keycode);
}
float track;
@Override
public void render () {
track += Gdx.graphics.getDeltaTime();
Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
cameraController.update(Gdx.graphics.getDeltaTime());
renderContext.begin();
frameBuffer.begin();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
renerablePool.flush();
renderables.clear();
modelCache.begin(camera);
modelCache.add(cannon);
modelCache.add(floorInstance);
for (Light light : lights) {
light.update(Gdx.graphics.getDeltaTime());
modelCache.add(light.lightInstance);
}
modelCache.end();
modelCache.getRenderables(renderables, renerablePool);
for (Renderable renderable : renderables) {
renderable.shader = shaderProvider.getShader(renderable);
}
renderableSorter.sort(camera, renderables);
Shader currentShader = null;
for (int i = 0; i < renderables.size; i++) {
final Renderable renderable = renderables.get(i);
if (currentShader != renderable.shader) {
if (currentShader != null) currentShader.end();
currentShader = renderable.shader;
currentShader.begin(camera, renderContext);
}
currentShader.render(renderable);
}
if (currentShader != null) currentShader.end();
frameBuffer.end();
mrtSceneShader.begin();
mrtSceneShader.setUniformi("u_diffuseTexture",
renderContext.textureBinder.bind(frameBuffer.getColorBufferTexture(DIFFUSE_ATTACHMENT)));
mrtSceneShader.setUniformi("u_normalTexture",
renderContext.textureBinder.bind(frameBuffer.getColorBufferTexture(NORMAL_ATTACHMENT)));
mrtSceneShader.setUniformi("u_positionTexture",
renderContext.textureBinder.bind(frameBuffer.getColorBufferTexture(POSITION_ATTACHMENT)));
mrtSceneShader.setUniformi("u_depthTexture", renderContext.textureBinder.bind(frameBuffer.getColorBufferTexture(DEPTH_ATTACHMENT)));
for (int i = 0; i < lights.size; i++) {
Light light = lights.get(i);
mrtSceneShader.setUniformf("lights[" + i + "].lightPosition", light.position);
mrtSceneShader.setUniformf("lights[" + i + "].lightColor", light.color);
}
mrtSceneShader.setUniformf("u_viewPos", camera.position);
mrtSceneShader.setUniformMatrix("u_inverseProjectionMatrix", camera.invProjectionView);
quad.render(mrtSceneShader, GL30.GL_TRIANGLE_FAN);
mrtSceneShader.end();
renderContext.end();
batch.disableBlending();
batch.begin();
batch.draw(frameBuffer.getColorBufferTexture(DIFFUSE_ATTACHMENT), 0, 0, Gdx.graphics.getWidth() / 4f,
Gdx.graphics.getHeight() / 4f, 0f, 0f, 1f, 1f);
batch.draw(frameBuffer.getColorBufferTexture(NORMAL_ATTACHMENT), Gdx.graphics.getWidth() / 4f, 0,
Gdx.graphics.getWidth() / 4f, Gdx.graphics.getHeight() / 4f, 0f, 0f, 1f, 1f);
batch.draw(frameBuffer.getColorBufferTexture(POSITION_ATTACHMENT), 2 * Gdx.graphics.getWidth() / 4f, 0,
Gdx.graphics.getWidth() / 4f, Gdx.graphics.getHeight() / 4f, 0f, 0f, 1f, 1f);
batch.draw(frameBuffer.getColorBufferTexture(DEPTH_ATTACHMENT), 3 * Gdx.graphics.getWidth() / 4f, 0,
Gdx.graphics.getWidth() / 4f, Gdx.graphics.getHeight() / 4f, 0f, 0f, 1f, 1f);
batch.end();
}
@Override
public void dispose () {
frameBuffer.dispose();
batch.dispose();
cannon.model.dispose();
floorInstance.model.dispose();
for (Light light : lights) {
light.lightInstance.model.dispose();
}
mrtSceneShader.dispose();
quad.dispose();
}
public Mesh createFullScreenQuad () {
float[] verts = new float[20];
int i = 0;
verts[i++] = -1;
verts[i++] = -1;
verts[i++] = 0;
verts[i++] = 0f;
verts[i++] = 0f;
verts[i++] = 1f;
verts[i++] = -1;
verts[i++] = 0;
verts[i++] = 1f;
verts[i++] = 0f;
verts[i++] = 1f;
verts[i++] = 1f;
verts[i++] = 0;
verts[i++] = 1f;
verts[i++] = 1f;
verts[i++] = -1;
verts[i++] = 1f;
verts[i++] = 0;
verts[i++] = 0f;
verts[i++] = 1f;
Mesh mesh = new Mesh(true, 4, 0,
new VertexAttribute(VertexAttributes.Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE),
new VertexAttribute(VertexAttributes.Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE + "0"));
mesh.setVertices(verts);
return mesh;
}
static class Light {
Vector3 position = new Vector3();
Vector3 color = new Vector3();
ModelInstance lightInstance;
float vy;
float vx;
float vz;
public void update (float deltaTime) {
vy += -30f * deltaTime;
position.y += vy * deltaTime;
position.x += vx * deltaTime;
position.z += vz * deltaTime;
if (position.y < 0.1f) {
vy *= -0.70f;
position.y = 0.1f;
}
if (position.x < -5) {
vx = -vx;
position.x = -5;
}
if (position.x > 5) {
vx = -vx;
position.x = 5;
}
if (position.z < -5) {
vz = -vz;
position.z = -5;
}
if (position.z > 5) {
vz = -vz;
position.z = 5;
}
lightInstance.transform.setToTranslation(position);
}
}
static class MRTShader implements Shader {
ShaderProgram shaderProgram;
long attributes;
RenderContext context;
Matrix3 matrix3 = new Matrix3();
static Attributes tmpAttributes = new Attributes();
public MRTShader (Renderable renderable) {
String prefix = "";
if (renderable.material.has(TextureAttribute.Normal)) {
prefix += "#define texturedFlag\n";
}
String vert = Gdx.files.internal("data/g3d/shaders/mrt.vert").readString();
String frag = Gdx.files.internal("data/g3d/shaders/mrt.frag").readString();
shaderProgram = new ShaderProgram(prefix + vert, prefix + frag);
if (!shaderProgram.isCompiled()) {
throw new GdxRuntimeException(shaderProgram.getLog());
}
renderable.material.set(tmpAttributes);
attributes = tmpAttributes.getMask();
}
@Override
public void init () {
}
@Override
public int compareTo (Shader other) {
//quick and dirty shader sort
if (((MRTShader) other).attributes == attributes) return 0;
if ((((MRTShader) other).attributes & TextureAttribute.Normal) == 1) return -1;
return 1;
}
@Override
public boolean canRender (Renderable instance) {
return attributes == instance.material.getMask();
}
@Override
public void begin (Camera camera, RenderContext context) {
this.context = context;
shaderProgram.begin();
shaderProgram.setUniformMatrix("u_projViewTrans", camera.combined);
context.setDepthTest(GL20.GL_LEQUAL);
context.setCullFace(GL20.GL_BACK);
}
@Override
public void render (Renderable renderable) {
Material material = renderable.material;
TextureAttribute diffuseTexture = (TextureAttribute)material.get(TextureAttribute.Diffuse);
TextureAttribute normalTexture = (TextureAttribute)material.get(TextureAttribute.Normal);
TextureAttribute specTexture = (TextureAttribute)material.get(TextureAttribute.Specular);
if (diffuseTexture != null) {
shaderProgram.setUniformi("u_diffuseTexture", context.textureBinder.bind(diffuseTexture.textureDescription.texture));
}
if (normalTexture != null) {
shaderProgram.setUniformi("u_normalTexture", context.textureBinder.bind(normalTexture.textureDescription.texture));
}
if (specTexture != null) {
shaderProgram.setUniformi("u_specularTexture", context.textureBinder.bind(specTexture.textureDescription.texture));
}
shaderProgram.setUniformMatrix("u_worldTrans", renderable.worldTransform);
shaderProgram.setUniformMatrix("u_normalMatrix", matrix3.set(renderable.worldTransform).inv().transpose());
renderable.meshPart.render(shaderProgram);
}
@Override
public void end () {
}
@Override
public void dispose () {
shaderProgram.dispose();
}
}
static class MRTFrameBuffer implements Disposable {
/** the frame buffers **/
private final static Map<Application, Array<MRTFrameBuffer>> buffers = new HashMap<Application, Array<MRTFrameBuffer>>();
/** the color buffer texture **/
private Array<Texture> colorTextures;
/** the default framebuffer handle, a.k.a screen. */
private static int defaultFramebufferHandle;
/** true if we have polled for the default handle already. */
private static boolean defaultFramebufferHandleInitialized = false;
/** the framebuffer handle **/
private int framebufferHandle;
/** width **/
private final int width;
/** height **/
private final int height;
MRTFrameBuffer (int width, int height, int numColorAttachments) {
this.width = width;
this.height = height;
build();
addManagedFrameBuffer(Gdx.app, this);
}
private Texture createColorTexture (Texture.TextureFilter min, Texture.TextureFilter mag, int internalformat, int format,
int type) {
GLOnlyTextureData data = new GLOnlyTextureData(width, height, 0, internalformat, format, type);
Texture result = new Texture(data);
result.setFilter(min, mag);
result.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge);
return result;
}
private Texture createDepthTexture () {
GLOnlyTextureData data = new GLOnlyTextureData(width, height, 0, GL30.GL_DEPTH_COMPONENT32F, GL30.GL_DEPTH_COMPONENT,
GL30.GL_FLOAT);
Texture result = new Texture(data);
result.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
result.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge);
return result;
}
private void disposeColorTexture (Texture colorTexture) {
colorTexture.dispose();
}
private void build () {
GL20 gl = Gdx.gl20;
// iOS uses a different framebuffer handle! (not necessarily 0)
if (!defaultFramebufferHandleInitialized) {
defaultFramebufferHandleInitialized = true;
if (Gdx.app.getType() == Application.ApplicationType.iOS) {
IntBuffer intbuf = ByteBuffer.allocateDirect(16 * Integer.SIZE / 8).order(ByteOrder.nativeOrder())
.asIntBuffer();
gl.glGetIntegerv(GL20.GL_FRAMEBUFFER_BINDING, intbuf);
defaultFramebufferHandle = intbuf.get(0);
} else {
defaultFramebufferHandle = 0;
}
}
colorTextures = new Array<Texture>();
framebufferHandle = gl.glGenFramebuffer();
gl.glBindFramebuffer(GL20.GL_FRAMEBUFFER, framebufferHandle);
//rgba
Texture diffuse = createColorTexture(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest, GL30.GL_RGBA8,
GL30.GL_RGBA, GL30.GL_UNSIGNED_BYTE);
//rgb
Texture normal = createColorTexture(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest, GL30.GL_RGB8,
GL30.GL_RGB, GL30.GL_UNSIGNED_BYTE);
//rgb
Texture position = createColorTexture(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest, GL30.GL_RGB8,
GL30.GL_RGB, GL30.GL_UNSIGNED_BYTE);
Texture depth = createDepthTexture();
colorTextures.add(diffuse);
colorTextures.add(normal);
colorTextures.add(position);
colorTextures.add(depth);
gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL30.GL_TEXTURE_2D,
diffuse.getTextureObjectHandle(), 0);
gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT1, GL30.GL_TEXTURE_2D,
normal.getTextureObjectHandle(), 0);
gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT2, GL30.GL_TEXTURE_2D,
position.getTextureObjectHandle(), 0);
gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL20.GL_DEPTH_ATTACHMENT, GL20.GL_TEXTURE_2D,
depth.getTextureObjectHandle(), 0);
IntBuffer buffer = BufferUtils.newIntBuffer(3);
buffer.put(GL30.GL_COLOR_ATTACHMENT0);
buffer.put(GL30.GL_COLOR_ATTACHMENT1);
buffer.put(GL30.GL_COLOR_ATTACHMENT2);
buffer.position(0);
Gdx.gl30.glDrawBuffers(3, buffer);
gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, 0);
gl.glBindTexture(GL20.GL_TEXTURE_2D, 0);
int result = gl.glCheckFramebufferStatus(GL20.GL_FRAMEBUFFER);
gl.glBindFramebuffer(GL20.GL_FRAMEBUFFER, defaultFramebufferHandle);
if (result != GL20.GL_FRAMEBUFFER_COMPLETE) {
for (Texture colorTexture : colorTextures)
disposeColorTexture(colorTexture);
gl.glDeleteFramebuffer(framebufferHandle);
if (result == GL20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
throw new IllegalStateException("frame buffer couldn't be constructed: incomplete attachment");
if (result == GL20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS)
throw new IllegalStateException("frame buffer couldn't be constructed: incomplete dimensions");
if (result == GL20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
throw new IllegalStateException("frame buffer couldn't be constructed: missing attachment");
if (result == GL20.GL_FRAMEBUFFER_UNSUPPORTED)
throw new IllegalStateException("frame buffer couldn't be constructed: unsupported combination of formats");
throw new IllegalStateException("frame buffer couldn't be constructed: unknown error " + result);
}
}
/** Releases all resources associated with the FrameBuffer. */
@Override
public void dispose () {
GL20 gl = Gdx.gl20;
for (Texture textureAttachment : colorTextures) {
disposeColorTexture(textureAttachment);
}
gl.glDeleteFramebuffer(framebufferHandle);
if (buffers.get(Gdx.app) != null)
buffers.get(Gdx.app).removeValue(this, true);
}
/** Makes the frame buffer current so everything gets drawn to it. */
public void bind () {
Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, framebufferHandle);
}
/** Unbinds the framebuffer, all drawing will be performed to the normal framebuffer from here on. */
public static void unbind () {
Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0);
}
/** Binds the frame buffer and sets the viewport accordingly, so everything gets drawn to it. */
public void begin () {
bind();
setFrameBufferViewport();
}
/** Sets viewport to the dimensions of framebuffer. Called by {@link #begin()}. */
protected void setFrameBufferViewport () {
Gdx.gl20.glViewport(0, 0, colorTextures.first().getWidth(), colorTextures.first().getHeight());
}
/** Unbinds the framebuffer, all drawing will be performed to the normal framebuffer from here on. */
public void end () {
end(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
/** Unbinds the framebuffer and sets viewport sizes, all drawing will be performed to the normal framebuffer from here on.
*
* @param x the x-axis position of the viewport in pixels
* @param y the y-asis position of the viewport in pixels
* @param width the width of the viewport in pixels
* @param height the height of the viewport in pixels */
public void end (int x, int y, int width, int height) {
unbind();
Gdx.gl20.glViewport(x, y, width, height);
}
public Texture getColorBufferTexture (int index) {
return colorTextures.get(index);
}
/** @return the height of the framebuffer in pixels */
public int getHeight () {
return colorTextures.first().getHeight();
}
/** @return the width of the framebuffer in pixels */
public int getWidth () {
return colorTextures.first().getWidth();
}
/** @return the depth of the framebuffer in pixels (if applicable) */
public int getDepth () {
return colorTextures.first().getDepth();
}
private static void addManagedFrameBuffer (Application app, MRTFrameBuffer frameBuffer) {
Array<MRTFrameBuffer> managedResources = buffers.get(app);
if (managedResources == null)
managedResources = new Array<MRTFrameBuffer>();
managedResources.add(frameBuffer);
buffers.put(app, managedResources);
}
public static StringBuilder getManagedStatus (final StringBuilder builder) {
builder.append("Managed buffers/app: { ");
for (Application app : buffers.keySet()) {
builder.append(buffers.get(app).size);
builder.append(" ");
}
builder.append("}");
return builder;
}
public static String getManagedStatus () {
return getManagedStatus(new StringBuilder()).toString();
}
}
protected static class RenderablePool extends Pool<Renderable> {
protected Array<Renderable> obtained = new Array<Renderable>();
@Override
protected Renderable newObject () {
return new Renderable();
}
@Override
public Renderable obtain () {
Renderable renderable = super.obtain();
renderable.environment = null;
renderable.material = null;
renderable.meshPart.set("", null, 0, 0, 0);
renderable.shader = null;
obtained.add(renderable);
return renderable;
}
public void flush () {
super.freeAll(obtained);
obtained.clear();
}
}
}