/*
* Copyright Nicolas Brodu
* License: Blah blah blah. If this becomes part of the Xith3D project,
* it will use the project license. In the meantime, let's say it is LGPL.
*/
package com.xith3d.render.jogl;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import net.java.games.jogl.DefaultGLCapabilitiesChooser;
import net.java.games.jogl.GL;
import net.java.games.jogl.GLCapabilities;
import net.java.games.jogl.GLDrawable;
import net.java.games.jogl.impl.GLContext;
import net.java.games.jogl.impl.GLContextFactory;
import net.java.games.jogl.impl.GLPbufferImpl;
import com.xith3d.image.DirectBufferedImage;
import com.xith3d.render.RenderFrame;
import com.xith3d.render.RenderPeer;
import com.xith3d.utility.profile.ProfileTimer;
/**
* @author Nicolas Brodu
*
* This CanvasPeer renders into an Image made from a pbuffer => uses hardware acceleration
* Note that since is is copied back to an image, this is probably not what you want
* to make fast games.
* On the other hand, if all you want is visualize large scenes requirering HW rendering to
* compute, and with a nice Swing interface all around, this maybe worth a look.
*
* This canvas only renders the scene when updateImage is called. It will not render
* the scene when View.renderOnce() is called.
*
* TODO: Many improvements.
* Main idea would be to write directly to a VolatileImage. Indeed, as it is now,
* the rendering happens on a pbuffer in vram. But instead of a direct vram->vram
* operation as could be expected with a VolatileImage, the BufferedImage used here
* does vram->ram and then will do ram->vram again probably when the user displays the image.
* What a mess!
*/
public class PBufferCanvasPeer extends CanvasPeerImpl {
protected GLPbufferImpl pbuffer;
protected static GLContext ctx; // context of parent to pbuffer
protected GLCapabilities glc;
protected String title = "";
protected boolean refreshing = false;
protected DirectBufferedImage snapshotImage = null;
protected PBufferCanvas3D c3d;
protected boolean initialized = false;
public PBufferCanvasPeer(PBufferCanvas3D c3d, RenderPeer renderPeer, Object owner, int width, int height, int bits, boolean fullScreen, GLCapabilities gc) throws RuntimeException {
super(renderPeer, owner, width, height, bits, fullScreen, gc);
canvas.removeGLEventListener(this);
this.c3d = c3d;
glc = gc;
if (glc==null) glc = new GLCapabilities();
if (ctx==null) ctx = GLContextFactory.getFactory().createGLContext((Component)owner, glc, new DefaultGLCapabilitiesChooser(), null);
if (!ctx.canCreatePbufferContext()) throw new RuntimeException("PBuffers not supported.");
pbuffer = new GLPbufferImpl(ctx.createPbufferContext(glc,width,height));
pbuffer.addGLEventListener(this);
}
public static void initContext(Object owner) {
ctx = GLContextFactory.getFactory().createGLContext((Component)owner, new GLCapabilities(), new DefaultGLCapabilitiesChooser(), null);
}
public boolean isInitialized() {
if (initialized) return true;
initialized = pbuffer.isInitialized();
//System.out.println("pbuffer is "+(ret?"":"not ")+"initialized"+(ret?"!":""));
return initialized;
}
public void display(GLDrawable drawable) {
if (!refreshing) return;
canvas.setGL(drawable.getGL());
canvas.setGLU(drawable.getGLU());
super.display(pbuffer);
if (snapshotImage==null) snapshotImage = (DirectBufferedImage)DirectBufferedImage.getDirectImageRGB(width, height);
pbuffer.getGL().glReadPixels(0, 0, width, height, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, snapshotImage.getBackingStore());
refreshing = false; // one time display
}
public void init() {
// Force onscreen GL context to instanciate pbuffers.
ctx.invokeGL(new Runnable() {
public void run() {
//System.out.println("Run in context");
pbuffer.display();
}},false,new Runnable() {
public void run() {
//System.out.println("Init action");
}});
}
// parent override to render in the pbuffer, not the canvas
public void render(RenderFrame frame) {
ProfileTimer.startProfile("PBufferCanvasPeer::render");
renderFrame = frame;
pbuffer.display();
ProfileTimer.endProfile();
}
// parent override to render in the pbuffer, not the canvas
public void renderDebug(RenderFrame frame, String logfile) {
try {
debugStream = new PrintStream(new BufferedOutputStream(new FileOutputStream(logfile)));
} catch (FileNotFoundException e) {
e.printStackTrace();
return;
}
debugFrame = true;
pbuffer.display();
debugFrame = false;
debugStream.close();
}
public BufferedImage updateImage() {
if (!isInitialized()) {
init();
if (!isInitialized()) return null;
}
refreshing = true;
render(c3d.getView().getRenderFrame(c3d));
return snapshotImage;
}
public BufferedImage getLastImage() {
if (snapshotImage==null) updateImage();
return snapshotImage;
}
/**
* @see CanvasPeer#setTitle
*/
public void setTitle(String title) {
this.title = title;
}
/**
* {@inheritDoc}
*/
public String getTitle() {
return title;
}
/**
* {@inheritDoc}
*/
public void setSize(int width, int height) {
try {
pbuffer.setSize(width, height);
} catch(net.java.games.jogl.GLException gle) {
pbuffer.removeGLEventListener(this);
pbuffer = new GLPbufferImpl(ctx.createPbufferContext(glc,width,height));
initialized = false;
snapshotImage = null;
this.width = width; this.height = height;
pbuffer.addGLEventListener(this);
}
}
/**
* {@inheritDoc}
*/
public void setSize(Dimension d) {
setSize(d.width, d.height);
}
/**
* {@inheritDoc}
*/
public Dimension getSize() {
return pbuffer.getSize();
}
/**
* {@inheritDoc}
*/
public void setLocation(int x, int y) {
// TODO: link with shapes
}
/**
* {@inheritDoc}
*/
public void setLocation(Point p) {
// TODO: link with shapes
}
/**
* {@inheritDoc}
*/
public Point getLocation() {
return new Point(0,0);
}
}