package org.archstudio.bna.ui.jogl;
import java.awt.image.BufferedImage;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.GLRunnable;
import org.archstudio.bna.IBNAView;
import org.archstudio.bna.ui.utils.AbstractSWTUI;
import org.archstudio.bna.utils.BNAUtils;
import org.archstudio.sysutils.Finally;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import com.jogamp.opengl.swt.GLCanvas;
public class JOGLBNAUI extends AbstractSWTUI {
class RepaintThread {
private final Thread thread;
private boolean needsRepaint = false;
private boolean disposed = false;
public RepaintThread() {
thread = new Thread(new Runnable() {
@Override
public void run() {
while (!disposed) {
synchronized (RepaintThread.this) {
try {
if (!needsRepaint) {
RepaintThread.this.wait();
}
}
catch (InterruptedException e) {
}
needsRepaint = false;
}
try {
glCanvas.display();
// rate limit repaint
Thread.sleep(1000 / 30);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
});
thread.setName(RepaintThread.class.getName());
thread.setDaemon(true);
thread.start();
}
public void paint() {
needsRepaint = true;
synchronized (this) {
notifyAll();
}
}
public void dispose() {
disposed = true;
synchronized (this) {
notifyAll();
}
}
}
class BNAGLEventListener implements GLEventListener {
public BNAGLEventListener() {
}
@Override
public void init(GLAutoDrawable drawable) {
joglThreadResources = new JOGLResources(drawable.getGL().getGL2ES2());
}
@Override
public void dispose(GLAutoDrawable drawable) {
joglThreadResources.dispose();
}
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
}
@Override
public void display(GLAutoDrawable drawable) {
try (Finally lock = BNAUtils.lock()) {
Rectangle localBounds = new Rectangle(0, 0, drawable.getWidth(), drawable.getHeight());
loadPreferences(joglThreadResources, parent);
joglThreadResources.setLocalBounds(localBounds);
joglThreadResources.renderInit();
joglThreadResources.renderReshape(localBounds);
joglThreadResources.renderTopLevelThings(view,
new Rectangle(0, 0, drawable.getWidth(), drawable.getHeight()));
}
catch (Exception e) {
e.printStackTrace();
}
}
}
JOGLResources joglThreadResources;
Composite parent;
GLCanvas glCanvas;
RepaintThread repaintThread = new RepaintThread();
PaintListener paintListener;
public JOGLBNAUI(IBNAView view) {
super(view);
}
@Override
public void dispose() {
super.dispose();
repaintThread.dispose();
glCanvas.removePaintListener(paintListener);
if (!glCanvas.isDisposed()) {
glCanvas.dispose();
}
}
@Override
public void init(Composite parent, int style) {
this.parent = parent;
/*
* Feature in Mac OS X: Creating a Graphics2D object for a BufferedImage somehow corrupts the OpenGL drawable
* being rendered. Creating one here seems to prevent this corruption.
*/
new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR).createGraphics().dispose();
GLCapabilities caps;
caps = new GLCapabilities(GLProfile.getGL2ES2());
caps.setDoubleBuffered(true);
caps.setHardwareAccelerated(true);
caps.setSampleBuffers(true);
caps.setNumSamples(4);
glCanvas = new GLCanvas(parent, style, caps, null);
glCanvas.addPaintListener(paintListener = new PaintListener() {
/*
* This clears the display buffer so that old content does not flash up when, for instance, changing BNAUIs.
*/
boolean completedFirstPaint = false;
@Override
public void paintControl(PaintEvent e) {
if (!completedFirstPaint) {
completedFirstPaint = true;
e.gc.setBackground(e.gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
e.gc.fillRectangle(glCanvas.getClientArea());
glCanvas.display();
}
}
});
super.init(glCanvas, true);
view.setBNAUI(this);
glCanvas.addGLEventListener(new BNAGLEventListener());
}
@Override
public Composite getComposite() {
return glCanvas;
}
@Override
public void forceFocus() {
glCanvas.forceFocus();
}
@Override
public void paint() {
repaintThread.paint();
}
@Override
public BufferedImage render(final Rectangle localBounds) {
final BufferedImage[] image = new BufferedImage[1];
glCanvas.invoke(true, new GLRunnable() {
@Override
public boolean run(GLAutoDrawable drawable) {
loadPreferences(joglThreadResources, parent);
image[0] = joglThreadResources.renderToImage(view, localBounds);
return true;
}
});
return image[0];
}
}