There is a quick solution to your problem, that will work regardless of the scene setup, postprocessing queues, viewports etc… , since it works on raw framebuffers, but it uses engine’s internals, so it might break in future versions of the engine, if we were to change those internals.
If you are ok with this, the solution is to mirror-blit the output framebuffer into an auxiliary framebuffer and then blit the auxiliary framebuffer back into the output framebuffer.
This is the code:
import java.lang.reflect.Field;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.opengl.GL;
import com.jme3.renderer.opengl.GLFbo;
import com.jme3.renderer.opengl.GLRenderer;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format;
public class FrameBufferFlip {
private static GLFbo glfbo;
private static FrameBuffer mirrorFb;
private static void init(GLRenderer renderer) {
if(glfbo != null) return;
try{
for(Field f:renderer.getClass().getDeclaredFields()){
Class t=f.getType();
if(GLFbo.class.isAssignableFrom(t)){
f.setAccessible(true);
glfbo=(GLFbo)f.get(renderer);
}
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void flip(RenderManager rm,FrameBuffer inputFb,Format colorFormat,int width,int height){
if(!(rm.getRenderer() instanceof GLRenderer))throw new RuntimeException("Non GL renderer not supported by this method");
GLRenderer glRenderer=(GLRenderer)rm.getRenderer();
init(glRenderer); // Get engine internals
if(mirrorFb!=null&&(mirrorFb.getWidth()!=width||mirrorFb.getHeight()!=height||mirrorFb.getColorBuffer().getFormat()!=colorFormat)){
mirrorFb.dispose();
mirrorFb=null;
}
if(mirrorFb==null){
mirrorFb=new FrameBuffer(width,height,1);
mirrorFb.setColorBuffer(colorFormat);
}
if(inputFb != null && inputFb.isUpdateNeeded()) glRenderer.updateFrameBuffer(inputFb);
if(mirrorFb != null && mirrorFb.isUpdateNeeded()) glRenderer.updateFrameBuffer(mirrorFb);
// Flip on auxiliary fb
glfbo.glBindFramebufferEXT(GLFbo.GL_READ_FRAMEBUFFER_EXT,inputFb==null?0:inputFb.getId());
glfbo.glBindFramebufferEXT(GLFbo.GL_DRAW_FRAMEBUFFER_EXT,mirrorFb.getId());
glfbo.glBlitFramebufferEXT(
width, 0, 0, height,
0, 0, width, height,
GL.GL_COLOR_BUFFER_BIT, GL.GL_NEAREST);
// Blit back to original fb
glfbo.glBindFramebufferEXT(GLFbo.GL_READ_FRAMEBUFFER_EXT,mirrorFb.getId());
glfbo.glBindFramebufferEXT(GLFbo.GL_DRAW_FRAMEBUFFER_EXT,inputFb==null?0:inputFb.getId());
glfbo.glBlitFramebufferEXT(
0, 0, width, height,
0, 0, width, height,
GL.GL_COLOR_BUFFER_BIT, GL.GL_NEAREST);
}
}
The usage is
FrameBufferFlip.flip(
renderManager,
YOUR_OUTPUT_FRAMEBUFFER,
YOUR_OUTPUT_COLOR_FORMAT, //Must be the same format used in YOUR_OUTPUT_FRAMEBUFFER
SCREEN_WIDTH,
SCREEN_HEIGHT
);
Call this anywhere, after you are sure the previous draw calls have been called.
In a common SimpleApplication, you can call it by overriding the update(tpf) like so
@Override
public void update() {
super.update();
FrameBufferFlip.flip(renderManager,viewPort.getOutputFrameBuffer(),Format.RGB8,viewPort.getCamera().getWidth(),viewPort.getCamera().getHeight());
}