/**
* Copyright 2012 Jason Sorensen (sorensenj@smert.net)
*
* 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 net.smert.frameworkgl.opengl.fbo;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import net.smert.frameworkgl.opengl.FrameBufferObject;
import net.smert.frameworkgl.opengl.GL;
import net.smert.frameworkgl.opengl.Texture;
import net.smert.frameworkgl.opengl.constants.FrameBufferObjectAttachments;
import net.smert.frameworkgl.opengl.constants.TextureFormats;
import net.smert.frameworkgl.opengl.constants.TextureInternalFormats;
import net.smert.frameworkgl.opengl.constants.TextureTargets;
import net.smert.frameworkgl.utils.ListUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Jason
*/
public class FrameBufferObjectBuilder {
private final static Logger log = LoggerFactory.getLogger(FrameBufferObjectBuilder.class);
private int fboHeight;
private int fboWidth;
private int maxColorAttachments;
private int maxDepthAttachments;
private int maxStencilAttachments;
private int numberOfColorAttachments;
private int numberOfDepthAttachments;
private int numberOfStencilAttachments;
private int readBufferAttachment;
private FrameBufferObject frameBufferObject;
private IntBuffer multipleRenderTarget;
private final List<Texture> textures;
public FrameBufferObjectBuilder() {
textures = new ArrayList<>();
reset();
}
private void checkAttachmentForErrors(int attachment) {
if ((attachment >= FrameBufferObjectAttachments.COLOR_ATTACHMENT0)
&& (attachment <= FrameBufferObjectAttachments.COLOR_ATTACHMENT15)) {
if (numberOfColorAttachments == maxColorAttachments) {
throw new IllegalStateException("Max color attachments exceeded");
}
numberOfColorAttachments++;
} else if (attachment == FrameBufferObjectAttachments.DEPTH_ATTACHMENT) {
if (numberOfDepthAttachments == maxDepthAttachments) {
throw new IllegalStateException("Max depth attachments exceeded");
}
numberOfDepthAttachments++;
} else if (attachment == FrameBufferObjectAttachments.STENCIL_ATTACHMENT) {
if (numberOfDepthAttachments == maxDepthAttachments) {
throw new IllegalStateException("Max depth attachments exceeded");
}
if (numberOfStencilAttachments == maxStencilAttachments) {
throw new IllegalStateException("Max stencil attachments exceeded");
}
numberOfDepthAttachments++;
numberOfStencilAttachments++;
}
}
public FrameBufferObjectBuilder attachNewTexture() {
int attachment = FrameBufferObjectAttachments.COLOR_ATTACHMENT0 + textures.size();
// Select an attachment based on the texture internal format. Also configure
// the format and GL Type of the texture.
switch (GL.textureBuilder.getTextureInternalFormat()) {
case TextureInternalFormats.DEPTH_COMPONENT16:
attachment = FrameBufferObjectAttachments.DEPTH_ATTACHMENT;
GL.textureBuilder.setFormatDepthComponent();
GL.textureBuilder.setPrimitiveShort();
break;
case TextureInternalFormats.DEPTH_COMPONENT24:
case TextureInternalFormats.DEPTH_COMPONENT32:
attachment = FrameBufferObjectAttachments.DEPTH_ATTACHMENT;
GL.textureBuilder.setFormatDepthComponent();
GL.textureBuilder.setPrimitiveInt();
break;
case TextureInternalFormats.DEPTH24_STENCIL8:
attachment = FrameBufferObjectAttachments.STENCIL_ATTACHMENT;
GL.textureBuilder.setFormatDepthStencil();
GL.textureBuilder.setPrimitiveStencil();
break;
case TextureInternalFormats.R16F:
case TextureInternalFormats.R32F:
GL.textureBuilder.setFormatR();
GL.textureBuilder.setPrimitiveFloat();
break;
case TextureInternalFormats.RG16F:
case TextureInternalFormats.RG32F:
GL.textureBuilder.setFormatRG();
GL.textureBuilder.setPrimitiveFloat();
break;
case TextureInternalFormats.RGB10_A2:
GL.textureBuilder.setFormatRGBA();
GL.textureBuilder.setPrimitiveInt1010102();
break;
case TextureInternalFormats.RGB16F:
case TextureInternalFormats.RGB32F:
GL.textureBuilder.setFormatRGB();
GL.textureBuilder.setPrimitiveFloat();
break;
case TextureInternalFormats.RGB8:
GL.textureBuilder.setFormatRGB();
GL.textureBuilder.setPrimitiveByte();
break;
case TextureInternalFormats.RGBA16F:
case TextureInternalFormats.RGBA32F:
GL.textureBuilder.setFormatRGBA();
GL.textureBuilder.setPrimitiveFloat();
break;
case TextureInternalFormats.RGBA8:
GL.textureBuilder.setFormatRGBA();
GL.textureBuilder.setPrimitiveByte();
break;
default:
throw new IllegalArgumentException("Unknown texture internal format: "
+ GL.textureBuilder.getTextureInternalFormat());
}
checkAttachmentForErrors(attachment);
// Switch texture target of the FBO
switch (GL.textureBuilder.getTextureType()) {
case TextureTargets.TEXTURE_2D:
GL.fboHelper.setTextureTarget2D();
break;
case TextureTargets.TEXTURE_3D:
GL.fboHelper.setTextureTarget3D();
break;
case TextureTargets.TEXTURE_CUBE_MAP:
break;
}
// Build texture
GL.textureBuilder.
setHeight(fboHeight).
setWidth(fboWidth).
setClampingWrapRClampToEdge().
setClampingWrapSClampToEdge().
setClampingWrapTClampToEdge().
setNullData(true).
buildTexture();
// Create texture
Texture texture = GL.textureBuilder.createTexture(true);
// Add texture to list of textures
textures.add(texture);
// Attach texture to FBO
if (attachment == FrameBufferObjectAttachments.STENCIL_ATTACHMENT) {
GL.fboHelper.attachTexture(FrameBufferObjectAttachments.DEPTH_ATTACHMENT, texture.getTextureID());
}
GL.fboHelper.attachTexture(attachment, texture.getTextureID());
return this;
}
public FrameBufferObjectBuilder attachTexture(Texture texture) {
int attachment;
// Select an attachment based on the texture format
switch (GL.textureBuilder.getTextureFormat()) {
case TextureFormats.DEPTH_COMPONENT:
attachment = FrameBufferObjectAttachments.DEPTH_ATTACHMENT;
break;
case TextureFormats.DEPTH_STENCIL:
attachment = FrameBufferObjectAttachments.STENCIL_ATTACHMENT;
break;
case TextureFormats.R:
case TextureFormats.RG:
case TextureFormats.RGB:
case TextureFormats.RGBA:
attachment = FrameBufferObjectAttachments.COLOR_ATTACHMENT0 + textures.size();
break;
default:
throw new IllegalArgumentException("Unknown texture format: " + GL.textureBuilder.getTextureFormat());
}
checkAttachmentForErrors(attachment);
// Switch texture target of the FBO
switch (GL.textureBuilder.getTextureType()) {
case TextureTargets.TEXTURE_2D:
GL.fboHelper.setTextureTarget2D();
break;
case TextureTargets.TEXTURE_3D:
GL.fboHelper.setTextureTarget3D();
break;
case TextureTargets.TEXTURE_CUBE_MAP:
break;
}
// Reset texture builder
GL.textureBuilder.reset();
// Add texture to list of textures
textures.add(texture);
// Attach texture
if (attachment == FrameBufferObjectAttachments.STENCIL_ATTACHMENT) {
GL.fboHelper.attachTexture(FrameBufferObjectAttachments.DEPTH_ATTACHMENT, texture.getTextureID());
}
GL.fboHelper.attachTexture(attachment, texture.getTextureID());
return this;
}
public FrameBufferObjectBuilder buildBegin() {
// Create new FBO
frameBufferObject = GL.glFactory.createFrameBufferObject();
frameBufferObject.create();
// Bind FBO
GL.fboHelper.bind(frameBufferObject.getFboID());
return this;
}
public FrameBufferObjectBuilder buildEnd(String name) {
// Set FBO draw and read buffers
if (numberOfColorAttachments == 0) {
log.debug("Frame buffer object has no color buffers.");
GL.fboHelper.disableBuffers();
} else {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < numberOfColorAttachments; i++) {
list.add(FrameBufferObjectAttachments.COLOR_ATTACHMENT0 + i);
}
int[] intArray = ListUtils.ToPrimitiveIntArray(list);
multipleRenderTarget = GL.bufferHelper.createIntBuffer(intArray.length);
multipleRenderTarget.put(intArray).flip();
GL.fboHelper.drawBuffer(multipleRenderTarget);
GL.fboHelper.readBuffer(readBufferAttachment);
}
// Check to see if the FBO is complete (built successfully)
int frameBufferObjectStatus = GL.fboHelper.checkStatus();
if (frameBufferObjectStatus != FrameBufferObjectAttachments.FRAMEBUFFER_COMPLETE) {
throw new RuntimeException("Unable to create frame buffer object: " + frameBufferObjectStatus);
}
// Unbind FBO
GL.fboHelper.unbind();
log.info("Frame buffer object \"{}\" was successfully built.", name);
return this;
}
public FrameBufferObject createFrameBufferObject(boolean reset) {
FrameBufferObject temp = frameBufferObject;
if (reset == true) {
reset();
GL.textureBuilder.reset();
}
return temp;
}
public void init() {
maxColorAttachments = GL.o3.getMaxColorAttachments();
maxDepthAttachments = 1;
maxStencilAttachments = 1;
}
public final FrameBufferObjectBuilder reset() {
fboHeight = 0;
fboWidth = 0;
numberOfColorAttachments = 0;
numberOfDepthAttachments = 0;
numberOfStencilAttachments = 0;
readBufferAttachment = FrameBufferObjectAttachments.NONE;
frameBufferObject = null;
multipleRenderTarget = null;
textures.clear();
return this;
}
public FrameBufferObjectBuilder setFilterMagLinear() {
GL.textureBuilder.setFilterMagLinear();
return this;
}
public FrameBufferObjectBuilder setFilterMagNearest() {
GL.textureBuilder.setFilterMagNearest();
return this;
}
public FrameBufferObjectBuilder setFilterMinLinear() {
GL.textureBuilder.setFilterMinLinear();
return this;
}
public FrameBufferObjectBuilder setFilterMinLinearMipmapLinear() {
GL.textureBuilder.setFilterMinLinearMipmapLinear();
return this;
}
public FrameBufferObjectBuilder setFilterMinLinearMipmapNearest() {
GL.textureBuilder.setFilterMinLinearMipmapNearest();
return this;
}
public FrameBufferObjectBuilder setFilterMinNearest() {
GL.textureBuilder.setFilterMinNearest();
return this;
}
public FrameBufferObjectBuilder setFilterMinNearestMipmapLinear() {
GL.textureBuilder.setFilterMinNearestMipmapLinear();
return this;
}
public FrameBufferObjectBuilder setFilterMinNearestMipmapNearest() {
GL.textureBuilder.setFilterMinNearestMipmapNearest();
return this;
}
public FrameBufferObjectBuilder setFormatDepthComponent() {
GL.textureBuilder.setFormatDepthComponent();
return this;
}
public FrameBufferObjectBuilder setFormatDepthStencil() {
GL.textureBuilder.setFormatDepthStencil();
return this;
}
public FrameBufferObjectBuilder setFormatR() {
GL.textureBuilder.setFormatR();
return this;
}
public FrameBufferObjectBuilder setFormatRG() {
GL.textureBuilder.setFormatRG();
return this;
}
public FrameBufferObjectBuilder setFormatRGB() {
GL.textureBuilder.setFormatRGB();
return this;
}
public FrameBufferObjectBuilder setFormatRGBA() {
GL.textureBuilder.setFormatRGBA();
return this;
}
public FrameBufferObjectBuilder setHeight(int height) {
this.fboHeight = height;
return this;
}
public FrameBufferObjectBuilder setInternalFormatDepthComponent16() {
GL.textureBuilder.setInternalFormatDepthComponent16();
return this;
}
public FrameBufferObjectBuilder setInternalFormatDepthComponent24() {
GL.textureBuilder.setInternalFormatDepthComponent24();
return this;
}
public FrameBufferObjectBuilder setInternalFormatDepthComponent32() {
GL.textureBuilder.setInternalFormatDepthComponent32();
return this;
}
public FrameBufferObjectBuilder setInternalFormatDepth24Stencil8() {
GL.textureBuilder.setInternalFormatDepth24Stencil8();
return this;
}
public FrameBufferObjectBuilder setInternalFormatR16F() {
GL.textureBuilder.setInternalFormatR16F();
return this;
}
public FrameBufferObjectBuilder setInternalFormatR32F() {
GL.textureBuilder.setInternalFormatR32F();
return this;
}
public FrameBufferObjectBuilder setInternalFormatRG16F() {
GL.textureBuilder.setInternalFormatRG16F();
return this;
}
public FrameBufferObjectBuilder setInternalFormatRG32F() {
GL.textureBuilder.setInternalFormatRG32F();
return this;
}
public FrameBufferObjectBuilder setInternalFormatRGB10_A2() {
GL.textureBuilder.setInternalFormatRGB10_A2();
return this;
}
public FrameBufferObjectBuilder setInternalFormatRGB16F() {
GL.textureBuilder.setInternalFormatRGB16F();
return this;
}
public FrameBufferObjectBuilder setInternalFormatRGB32F() {
GL.textureBuilder.setInternalFormatRGB32F();
return this;
}
public FrameBufferObjectBuilder setInternalFormatRGB8() {
GL.textureBuilder.setInternalFormatRGB8();
return this;
}
public FrameBufferObjectBuilder setInternalFormatRGBA16F() {
GL.textureBuilder.setInternalFormatRGBA16F();
return this;
}
public FrameBufferObjectBuilder setInternalFormatRGBA32F() {
GL.textureBuilder.setInternalFormatRGBA32F();
return this;
}
public FrameBufferObjectBuilder setInternalFormatRGBA8() {
GL.textureBuilder.setInternalFormatRGBA8();
return this;
}
public FrameBufferObjectBuilder setReadBufferAttachmentColorAttachment0() {
readBufferAttachment = FrameBufferObjectAttachments.COLOR_ATTACHMENT0;
return this;
}
public FrameBufferObjectBuilder setReadBufferAttachmentNone() {
readBufferAttachment = FrameBufferObjectAttachments.NONE;
return this;
}
public FrameBufferObjectBuilder setTextureTarget2D() {
GL.fboHelper.setTextureTarget2D();
GL.textureBuilder.setTextureTarget2D();
return this;
}
public FrameBufferObjectBuilder setTextureTarget3D() {
GL.fboHelper.setTextureTarget3D();
GL.textureBuilder.setTextureTarget3D();
return this;
}
public FrameBufferObjectBuilder setTextureTargetCubeMap() {
GL.fboHelper.setTextureTargetCubeMap();
GL.textureBuilder.setTextureTargetCubeMap();
return this;
}
public FrameBufferObjectBuilder setTextureTargetCubeMapNegativeX() {
GL.fboHelper.setTextureTargetCubeMapNegativeX();
return this;
}
public FrameBufferObjectBuilder setTextureTargetCubeMapPositiveX() {
GL.fboHelper.setTextureTargetCubeMapPositiveX();
return this;
}
public FrameBufferObjectBuilder setTextureTargetCubeMapNegativeY() {
GL.fboHelper.setTextureTargetCubeMapNegativeY();
return this;
}
public FrameBufferObjectBuilder setTextureTargetCubeMapPositiveY() {
GL.fboHelper.setTextureTargetCubeMapPositiveY();
return this;
}
public FrameBufferObjectBuilder setTextureTargetCubeMapNegativeZ() {
GL.fboHelper.setTextureTargetCubeMapNegativeZ();
return this;
}
public FrameBufferObjectBuilder setTextureTargetCubeMapPositiveZ() {
GL.fboHelper.setTextureTargetCubeMapPositiveZ();
return this;
}
public FrameBufferObjectBuilder setUseMipmaps(boolean enabled) {
GL.textureBuilder.setUseMipmaps(enabled);
return this;
}
public FrameBufferObjectBuilder setWidth(int width) {
this.fboWidth = width;
return this;
}
}