All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.texture.FrameBuffer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2009-2023 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme3.texture;

import com.jme3.renderer.Caps;
import com.jme3.renderer.Renderer;
import com.jme3.texture.Image.Format;
import com.jme3.util.NativeObject;
import java.util.ArrayList;

/**
 * 

* FrameBuffers are rendering surfaces allowing * off-screen rendering and render-to-texture functionality. * Instead of the scene rendering to the screen, it is rendered into the * FrameBuffer, the result can be either a texture or a buffer. *

* A FrameBuffer supports two methods of rendering, * using a {@link Texture} or using a buffer. * When using a texture, the result of the rendering will be rendered * onto the texture, after which the texture can be placed on an object * and rendered as if the texture was uploaded from disk. * When using a buffer, the result is rendered onto * a buffer located on the GPU, the data of this buffer is not accessible * to the user. buffers are useful if one * wishes to retrieve only the color content of the scene, but still desires * depth testing (which requires a depth buffer). * Buffers can be copied to other framebuffers * including the main screen, by using * {@link Renderer#copyFrameBuffer(com.jme3.texture.FrameBuffer, com.jme3.texture.FrameBuffer, boolean, boolean)}. * The content of a {@link RenderBuffer} can be retrieved by using * {@link Renderer#readFrameBuffer(com.jme3.texture.FrameBuffer, java.nio.ByteBuffer) }. *

* FrameBuffers have several attachment points, there are * several color attachment points and a single depth * attachment point. * The color attachment points support image formats such as * {@link Format#RGBA8}, allowing rendering the color content of the scene. * The depth attachment point requires a depth image format. * * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer) * * @author Kirill Vainer */ public class FrameBuffer extends NativeObject { public static final int SLOT_UNDEF = -1; public static final int SLOT_DEPTH = -100; public static final int SLOT_DEPTH_STENCIL = -101; private int width = 0; private int height = 0; private int samples = 1; final private ArrayList colorBufs = new ArrayList<>(); private RenderBuffer depthBuf = null; private int colorBufIndex = 0; private boolean srgb; private String name; private Boolean mipMapsGenerationHint = null; /** * RenderBuffer represents either a texture or a * buffer that will be rendered to. RenderBuffers * are attached to an attachment slot on a FrameBuffer. */ public static class RenderBuffer { Texture tex; Image.Format format; int id = -1; int slot = SLOT_UNDEF; int face = -1; int layer = -1; int level = 0; public int getLevel() { return this.level; } /** * @return The image format of the render buffer. */ public Format getFormat() { return format; } /** * @return The texture to render to for this RenderBuffer * or null if content should be rendered into a buffer. */ public Texture getTexture() { return tex; } /** * Do not use. * @return the buffer's ID */ public int getId() { return id; } /** * Do not use. * * @param id the desired ID */ public void setId(int id) { this.id = id; } /** * Do not use. * * @return the slot code, such as SLOT_DEPTH_STENCIL */ public int getSlot() { return slot; } public int getFace() { return face; } public void resetObject() { id = -1; } public RenderBuffer createDestructableClone() { if (tex != null) { return null; } else { RenderBuffer destructClone = new RenderBuffer(); destructClone.id = id; return destructClone; } } @Override public String toString() { if (tex != null) { return "TextureTarget[format=" + format + "]"; } else { return "BufferTarget[format=" + format + "]"; } } public int getLayer() { return this.layer; } } public static class FrameBufferTextureTarget extends RenderBuffer { private FrameBufferTextureTarget(){} void setTexture(Texture tx){ this.tex=tx; this.format=tx.getImage().getFormat(); } void setFormat(Format f){ this.format=f; } public FrameBufferTextureTarget layer(int i){ this.layer=i; return this; } public FrameBufferTextureTarget level(int i){ this.level=i; return this; } public FrameBufferTextureTarget face(TextureCubeMap.Face f){ return face(f.ordinal()); } public FrameBufferTextureTarget face(int f){ this.face=f; return this; } } public static class FrameBufferBufferTarget extends RenderBuffer { private FrameBufferBufferTarget(){} void setFormat(Format f){ this.format=f; } } public static class FrameBufferTarget { private FrameBufferTarget(){} public static FrameBufferTextureTarget newTarget(Texture tx){ FrameBufferTextureTarget t=new FrameBufferTextureTarget(); t.setTexture(tx); return t; } public static FrameBufferBufferTarget newTarget(Format format){ FrameBufferBufferTarget t=new FrameBufferBufferTarget(); t.setFormat(format); return t; } /** * Creates a frame buffer texture and sets the face position by using the face parameter. It uses * {@link TextureCubeMap} ordinal number for the face position. * * @param tx texture to add to the frame buffer * @param face face to add to the color buffer to * @return FrameBufferTexture Target */ public static FrameBufferTextureTarget newTarget(Texture tx, TextureCubeMap.Face face) { FrameBufferTextureTarget t = new FrameBufferTextureTarget(); t.face = face.ordinal(); t.setTexture(tx); return t; } } /** * A private constructor to inhibit instantiation of this class. */ private FrameBuffer() { } public void addColorTarget(FrameBufferBufferTarget colorBuf){ colorBuf.slot=colorBufs.size(); colorBufs.add(colorBuf); } public void addColorTarget(FrameBufferTextureTarget colorBuf){ // checkSetTexture(colorBuf.getTexture(), false); // TODO: this won't work for levels. colorBuf.slot=colorBufs.size(); colorBufs.add(colorBuf); } /** * Adds a texture to one of the color Buffers Array. It uses {@link TextureCubeMap} ordinal number for the * position in the color buffer ArrayList. * * @param colorBuf texture to add to the color Buffer * @param face position to add to the color buffer */ public void addColorTarget(FrameBufferTextureTarget colorBuf, TextureCubeMap.Face face) { // checkSetTexture(colorBuf.getTexture(), false); // TODO: this won't work for levels. colorBuf.slot = colorBufs.size(); colorBuf.face = face.ordinal(); colorBufs.add(colorBuf); } public void setDepthTarget(FrameBufferBufferTarget depthBuf){ if (!depthBuf.getFormat().isDepthFormat()) throw new IllegalArgumentException("Depth buffer format must be depth."); this.depthBuf = depthBuf; this.depthBuf.slot = this.depthBuf.getFormat().isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; } public void setDepthTarget(FrameBufferTextureTarget depthBuf){ checkSetTexture(depthBuf.getTexture(), true); this.depthBuf = depthBuf; this.depthBuf.slot = depthBuf.getTexture().getImage().getFormat().isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; } public int getNumColorTargets(){ return colorBufs.size(); } public RenderBuffer getColorTarget(int index){ return colorBufs.get(index); } public RenderBuffer getColorTarget() { if (colorBufs.isEmpty()) return null; if (colorBufIndex<0 || colorBufIndex>=colorBufs.size()) { return colorBufs.get(0); } return colorBufs.get(colorBufIndex); } public RenderBuffer getDepthTarget() { return depthBuf; } /** *

* Creates a new FrameBuffer with the given width, height, and number * of samples. If any textures are attached to this FrameBuffer, then * they must have the same number of samples as given in this constructor. *

* Note that if the {@link Renderer} does not expose the * {@link Caps#NonPowerOfTwoTextures}, then an exception will be thrown * if the width and height arguments are not power of two. * * @param width The width to use * @param height The height to use * @param samples The number of samples to use for a multisampled * framebuffer, or 1 if the framebuffer should be single-sampled. * * @throws IllegalArgumentException If width or height are not positive. */ public FrameBuffer(int width, int height, int samples) { super(); if (width <= 0 || height <= 0) { throw new IllegalArgumentException("FrameBuffer must have valid size."); } this.width = width; this.height = height; this.samples = samples == 0 ? 1 : samples; } protected FrameBuffer(FrameBuffer src) { super(src.id); /* for (RenderBuffer renderBuf : src.colorBufs){ RenderBuffer clone = renderBuf.createDestructableClone(); if (clone != null) this.colorBufs.add(clone); } this.depthBuf = src.depthBuf.createDestructableClone(); */ } /** * Enables the use of a depth buffer for this FrameBuffer. * * @param format The format to use for the depth buffer. * @throws IllegalArgumentException If format is not a depth format. * @deprecated Use * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferBufferTarget)} */ @Deprecated public void setDepthBuffer(Image.Format format) { if (id != -1) { throw new UnsupportedOperationException("FrameBuffer already initialized."); } if (!format.isDepthFormat()) { throw new IllegalArgumentException("Depth buffer format must be depth."); } depthBuf = new RenderBuffer(); depthBuf.slot = format.isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; depthBuf.format = format; } /** * Enables the use of a color buffer for this FrameBuffer. * * @param format The format to use for the color buffer. * @throws IllegalArgumentException If format is not a color format. * @deprecated Use addColorTarget */ @Deprecated public void setColorBuffer(Image.Format format) { if (id != -1) { throw new UnsupportedOperationException("FrameBuffer already initialized."); } if (format.isDepthFormat()) { throw new IllegalArgumentException("Color buffer format must be color/luminance."); } RenderBuffer colorBuf = new RenderBuffer(); colorBuf.slot = 0; colorBuf.format = format; colorBufs.clear(); colorBufs.add(colorBuf); } private void checkSetTexture(Texture tex, boolean depth) { Image img = tex.getImage(); if (img == null) { throw new IllegalArgumentException("Texture not initialized with RTT."); } if (depth && !img.getFormat().isDepthFormat()) { throw new IllegalArgumentException("Texture image format must be depth."); } else if (!depth && img.getFormat().isDepthFormat()) { throw new IllegalArgumentException("Texture image format must be color/luminance."); } // check that resolution matches texture resolution if (width != img.getWidth() || height != img.getHeight()) { throw new IllegalArgumentException("Texture image resolution " + "must match FB resolution"); } if (samples != tex.getImage().getMultiSamples()) { throw new IllegalStateException("Texture samples must match framebuffer samples"); } } /** * If enabled, any shaders rendering into this FrameBuffer * will be able to write several results into the renderbuffers * by using the gl_FragData array. Every slot in that * array maps into a color buffer attached to this framebuffer. * * @param enabled True to enable MRT (multiple rendering targets). */ public void setMultiTarget(boolean enabled) { if (enabled) { colorBufIndex = -1; } else { colorBufIndex = 0; } } /** * @return True if MRT (multiple rendering targets) is enabled. * @see FrameBuffer#setMultiTarget(boolean) */ public boolean isMultiTarget() { return colorBufIndex == -1; } /** * If MRT is not enabled ({@link FrameBuffer#setMultiTarget(boolean) } is false) * then this specifies the color target to which the scene should be rendered. *

* By default the value is 0. * * @param index The color attachment index. * @throws IllegalArgumentException If index is negative or doesn't map * to any attachment on this framebuffer. */ public void setTargetIndex(int index) { if (index < 0 || index >= 16) { throw new IllegalArgumentException("Target index must be between 0 and 16"); } if (colorBufs.size() < index) { throw new IllegalArgumentException("The target at " + index + " is not set!"); } colorBufIndex = index; setUpdateNeeded(); } /** * @return The color target to which the scene should be rendered. * * @see FrameBuffer#setTargetIndex(int) */ public int getTargetIndex() { return colorBufIndex; } /** * Set the color texture to use for this framebuffer. * This automatically clears all existing textures added previously * with {@link FrameBuffer#addColorTexture } and adds this texture as the * only target. * * @param tex The color texture to set. * @deprecated Use addColorTarget */ @Deprecated public void setColorTexture(Texture2D tex) { clearColorTargets(); addColorTexture(tex); } /** * Set the color texture array to use for this framebuffer. * This automatically clears all existing textures added previously * with {@link FrameBuffer#addColorTexture } and adds this texture as the * only target. * * @param tex The color texture array to set. * @param layer (default=-1) * @deprecated Use addColorTarget */ @Deprecated public void setColorTexture(TextureArray tex, int layer) { clearColorTargets(); addColorTexture(tex, layer); } /** * Set the color texture to use for this framebuffer. * This automatically clears all existing textures added previously * with {@link FrameBuffer#addColorTexture } and adds this texture as the * only target. * * @param tex The cube-map texture to set. * @param face The face of the cube-map to render to. * @deprecated Use addColorTarget */ @Deprecated public void setColorTexture(TextureCubeMap tex, TextureCubeMap.Face face) { clearColorTargets(); addColorTexture(tex, face); } /** * Clears all color targets that were set or added previously. */ public void clearColorTargets() { colorBufs.clear(); } /** * Add a color buffer without a texture bound to it. * If MRT is enabled, then each subsequently added texture or buffer can be * rendered to through a shader that writes to the array gl_FragData. * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) } * is rendered to by the shader. * * @param format the format of the color buffer * @see #addColorTexture(com.jme3.texture.Texture2D) * @deprecated Use addColorTarget */ @Deprecated public void addColorBuffer(Image.Format format) { if (id != -1) { throw new UnsupportedOperationException("FrameBuffer already initialized."); } if (format.isDepthFormat()) { throw new IllegalArgumentException("Color buffer format must be color/luminance."); } RenderBuffer colorBuf = new RenderBuffer(); colorBuf.slot = colorBufs.size(); colorBuf.format = format; colorBufs.add(colorBuf); } /** * Add a color texture to use for this framebuffer. * If MRT is enabled, then each subsequently added texture can be * rendered to through a shader that writes to the array gl_FragData. * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) } * is rendered to by the shader. * * @param tex The texture to add. * @see #addColorBuffer(com.jme3.texture.Image.Format) * @deprecated Use addColorTarget */ @Deprecated public void addColorTexture(Texture2D tex) { if (id != -1) { throw new UnsupportedOperationException("FrameBuffer already initialized."); } Image img = tex.getImage(); checkSetTexture(tex, false); RenderBuffer colorBuf = new RenderBuffer(); colorBuf.slot = colorBufs.size(); colorBuf.tex = tex; colorBuf.format = img.getFormat(); colorBufs.add(colorBuf); } /** * Add a color texture array to use for this framebuffer. * If MRT is enabled, then each subsequently added texture can be * rendered to through a shader that writes to the array gl_FragData. * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) } * is rendered to by the shader. * * @param tex The texture array to add. * @param layer (default=-1) * @deprecated Use addColorTarget */ @Deprecated public void addColorTexture(TextureArray tex, int layer) { if (id != -1) { throw new UnsupportedOperationException("FrameBuffer already initialized."); } Image img = tex.getImage(); checkSetTexture(tex, false); RenderBuffer colorBuf = new RenderBuffer(); colorBuf.slot = colorBufs.size(); colorBuf.tex = tex; colorBuf.format = img.getFormat(); colorBuf.layer = layer; colorBufs.add(colorBuf); } /** * Add a color texture to use for this framebuffer. * If MRT is enabled, then each subsequently added texture can be * rendered to through a shader that writes to the array gl_FragData. * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) } * is rendered to by the shader. * * @param tex The cube-map texture to add. * @param face The face of the cube-map to render to. * @deprecated Use addColorTarget */ @Deprecated public void addColorTexture(TextureCubeMap tex, TextureCubeMap.Face face) { if (id != -1) { throw new UnsupportedOperationException("FrameBuffer already initialized."); } Image img = tex.getImage(); checkSetTexture(tex, false); RenderBuffer colorBuf = new RenderBuffer(); colorBuf.slot = colorBufs.size(); colorBuf.tex = tex; colorBuf.format = img.getFormat(); colorBuf.face = face.ordinal(); colorBufs.add(colorBuf); } /** * Set the depth texture to use for this framebuffer. * * @param tex The color texture to set. * @deprecated Use * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferTextureTarget)} */ @Deprecated public void setDepthTexture(Texture2D tex) { if (id != -1) { throw new UnsupportedOperationException("FrameBuffer already initialized."); } Image img = tex.getImage(); checkSetTexture(tex, true); depthBuf = new RenderBuffer(); depthBuf.slot = img.getFormat().isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; depthBuf.tex = tex; depthBuf.format = img.getFormat(); } /** * * @param tex the TextureArray to apply * @param layer (default=-1) * @deprecated Use * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferTextureTarget)} */ @Deprecated public void setDepthTexture(TextureArray tex, int layer) { if (id != -1) { throw new UnsupportedOperationException("FrameBuffer already initialized."); } Image img = tex.getImage(); checkSetTexture(tex, true); depthBuf = new RenderBuffer(); depthBuf.slot = img.getFormat().isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; depthBuf.tex = tex; depthBuf.format = img.getFormat(); depthBuf.layer = layer; } /** * @return The number of color buffers attached to this texture. * @deprecated Use getNumColorTargets */ @Deprecated public int getNumColorBuffers() { return colorBufs.size(); } /** * @param index the zero-base index (≥0) * @return The color buffer at the given index. * @deprecated Use getColorTarget(int) */ @Deprecated public RenderBuffer getColorBuffer(int index) { return colorBufs.get(index); } /** * @return The color buffer with the index set by {@link #setTargetIndex(int)}, or null * if no color buffers are attached. * If MRT is disabled, the first color buffer is returned. * @deprecated Use getColorTarget() */ @Deprecated public RenderBuffer getColorBuffer() { if (colorBufs.isEmpty()) { return null; } if (colorBufIndex < 0 || colorBufIndex >= colorBufs.size()) { return colorBufs.get(0); } return colorBufs.get(colorBufIndex); } /** * @return The depth buffer attached to this FrameBuffer, or null * if no depth buffer is attached * @deprecated Use getDepthTarget() */ @Deprecated public RenderBuffer getDepthBuffer() { return depthBuf; } /** * @return The height in pixels of this framebuffer. */ public int getHeight() { return height; } /** * @return The width in pixels of this framebuffer. */ public int getWidth() { return width; } /** * @return The number of samples when using a multisample framebuffer, or * 1 if this is a single-sampled framebuffer. */ public int getSamples() { return samples; } @Override public String toString() { StringBuilder sb = new StringBuilder(); String mrtStr = colorBufIndex >= 0 ? "" + colorBufIndex : "mrt"; sb.append("FrameBuffer[format=").append(width).append("x").append(height) .append("x").append(samples).append(", drawBuf=").append(mrtStr).append("]\n"); if (depthBuf != null) { sb.append("Depth => ").append(depthBuf).append("\n"); } for (RenderBuffer colorBuf : colorBufs) { sb.append("Color(").append(colorBuf.slot) .append(") => ").append(colorBuf).append("\n"); } return sb.toString(); } @Override public void resetObject() { this.id = -1; for (int i = 0; i < colorBufs.size(); i++) { colorBufs.get(i).resetObject(); } if (depthBuf != null) { depthBuf.resetObject(); } setUpdateNeeded(); } @Override public void deleteObject(Object rendererObject) { ((Renderer) rendererObject).deleteFrameBuffer(this); } @Override public NativeObject createDestructableClone() { return new FrameBuffer(this); } @Override public long getUniqueId() { return ((long) OBJTYPE_FRAMEBUFFER << 32) | (0xffffffffL & (long) id); } /** * Specifies that the color values stored in this framebuffer are in SRGB * format. * * The FrameBuffer must have an SRGB texture attached. * * The Renderer must expose the {@link Caps#Srgb sRGB pipeline} capability * for this option to take any effect. * * Rendering operations performed on this framebuffer shall undergo a linear * -> sRGB color space conversion when this flag is enabled. If * {@link com.jme3.material.RenderState#getBlendMode() blending} is enabled, it will be * performed in linear space by first decoding the stored sRGB pixel values * into linear, combining with the shader result, and then converted back to * sRGB upon being written into the framebuffer. * * @param srgb If the framebuffer color values should be stored in sRGB * color space. * * @throws IllegalStateException If the texture attached to this framebuffer * is not sRGB. */ public void setSrgb(boolean srgb) { this.srgb = srgb; } /** * Determines if this framebuffer contains SRGB data. * * @return True if the framebuffer color values are in SRGB space, false if * in linear space. */ public boolean isSrgb() { return srgb; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * Hints the renderer to generate mipmaps for this framebuffer if necessary * @param v true to enable, null to use the default value for the renderer (default to null) */ public void setMipMapsGenerationHint(Boolean v) { mipMapsGenerationHint = v; } public Boolean getMipMapsGenerationHint() { return mipMapsGenerationHint; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy