package icy.vtk;
import icy.gui.dialog.IdConfirmDialog;
import icy.gui.dialog.MessageDialog;
import icy.gui.frame.progress.FailedAnnounceFrame;
import icy.system.IcyExceptionHandler;
import icy.system.IcyHandledException;
import icy.system.thread.ThreadUtil;
import icy.util.OpenGLUtil;
import icy.util.ReflectionUtil;
import java.awt.Graphics;
import java.util.concurrent.locks.ReentrantLock;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLJPanel;
import jogamp.opengl.GLDrawableHelper;
import vtk.vtkCamera;
import vtk.vtkGenericOpenGLRenderWindow;
import vtk.vtkGenericRenderWindowInteractor;
import vtk.vtkLight;
import vtk.vtkRenderWindow;
import vtk.vtkRenderWindowInteractor;
import vtk.vtkRenderer;
import vtk.vtkTIFFWriter;
import vtk.vtkWindowToImageFilter;
public class VtkJoglPanel extends GLJPanel
{
class GLEventImpl implements GLEventListener
{
@Override
public void init(GLAutoDrawable drawable)
{
if (!windowset)
{
windowset = true;
// Make sure the JOGL Context is current
GLContext ctx = drawable.getContext();
if (!ctx.isCurrent())
ctx.makeCurrent();
// Init VTK OpenGL RenderWindow
rw.SetMapped(1);
rw.SetPosition(0, 0);
setSize(drawable.getWidth(), drawable.getHeight());
rw.OpenGLInit();
// init light
if (!lightingset)
{
lightingset = true;
ren.AddLight(lgt);
lgt.SetPosition(cam.GetPosition());
lgt.SetFocalPoint(cam.GetFocalPoint());
}
}
}
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height)
{
setSize(width, height);
}
@Override
public void display(GLAutoDrawable drawable)
{
render();
}
@Override
public void dispose(GLAutoDrawable drawable)
{
delete();
}
}
/**
*
*/
private static final long serialVersionUID = 8821516677188995191L;
protected vtkGenericOpenGLRenderWindow rw;
protected vtkRenderer ren;
protected vtkRenderWindowInteractor wi;
protected vtkCamera cam;
protected vtkLight lgt;
protected ReentrantLock lock;
protected GLEventImpl glEventImpl;
protected int lastX;
protected int lastY;
protected boolean windowset;
protected boolean lightingset;
protected int interactionMode;
protected boolean rendering;
private boolean failed;
public VtkJoglPanel()
{
super(new GLCapabilities(GLProfile.getDefault()));
rw = new vtkGenericOpenGLRenderWindow();
// init render window
rw.SetIsDirect(1);
rw.SetSupportsOpenGL(1);
rw.SetIsCurrent(true);
// FIXME: smoothing is broken with VTK 6.3
// rw.SetPointSmoothing(1);
// rw.SetLineSmoothing(1);
// rw.SetPolygonSmoothing(1);
// rw.SetMultiSamples(4);
// init window interactor
wi = new vtkGenericRenderWindowInteractor();
wi.SetRenderWindow(rw);
wi.ConfigureEvent();
ren = new vtkRenderer();
ren.SetLightFollowCamera(1);
cam = null;
lgt = new vtkLight();
// set ambient color to white
lgt.SetAmbientColor(1d, 1d, 1d);
lock = new ReentrantLock();
glEventImpl = new GLEventImpl();
windowset = false;
lightingset = false;
rendering = false;
failed = false;
addGLEventListener(glEventImpl);
rw.AddRenderer(ren);
cam = ren.GetActiveCamera();
// super.setSize(200, 200);
// rw.SetSize(200, 200);
// not compatible with OpenGL 3 ? (new VTK OpenGL backend require OpenGL 3.2)
if (!OpenGLUtil.isOpenGLSupported(3))
{
if (!IdConfirmDialog
.confirm(
"Warning",
"Your graphics card driver does not support OpenGL 3, you may experience issues or crashes with VTK.\nDo you want to try anyway ?",
IdConfirmDialog.YES_NO_OPTION, getClass().getName() + ".notCompatibleDialog"))
throw new IcyHandledException("Your graphics card driver is not compatible with OpenGL 3 !");
}
}
/**
* @deprecated Use {@link #disposeInternal()} instead
*/
@Deprecated
public void Delete()
{
delete();
}
protected void delete()
{
if (rendering)
{
rw.SetAbortRender(1);
// wait a bit while rendering
ThreadUtil.sleep(500);
// still rendering --> exit
if (rendering)
return;
}
lock.lock();
try
{
// prevent any further rendering
rendering = true;
// if (getParent() != null)
// getParent().remove(this);
// release internal VTK objects
ren = null;
cam = null;
lgt = null;
// On linux we prefer to have a memory leak instead of a crash
if (!rw.GetClassName().equals("vtkXOpenGLRenderWindow"))
{
rw = null;
}
else
{
System.out.println("The renderwindow has been kept arount to prevent a crash");
}
// call it only once in parent as this can take a lot of time
// vtkObjectBase.JAVA_OBJECT_MANAGER.gc(false);
}
finally
{
// removing the renderWindow is let to the superclass
// because in the very special case of an AWT component
// under Linux, destroying renderWindow crashes.
lock.unlock();
}
}
/**
* Disable method, use {@link #disposeInternal()} instead to release VTK and OpenGL resources
*/
@Override
protected void dispose()
{
// prevent disposal on removeNotify as window externalization produce remove/add operation.
// --> don't forget to call disposeInternal when needed
}
/**
* Release VTK and OGL objects.<br>
* Call it when you know you won't use anymore the VTK OGL panel
*/
public void disposeInternal()
{
super.dispose();
// remove the GL event listener to avoid memory leak
removeGLEventListener(glEventImpl);
try
{
// hacky fix to avoid the infamous memory leak from ThreadLocal from GLPanel !
final GLDrawableHelper helper = (GLDrawableHelper) ReflectionUtil.getFieldObject(this, "helper", true);
final ThreadLocal threadLocal = (ThreadLocal) ReflectionUtil.getFieldObject(helper, "perThreadInitAction",
true);
threadLocal.remove();
}
catch (Throwable t)
{
// ignore
}
}
/**
* @deprecated Use {@link #lock()} instead
*/
@Deprecated
public void Lock()
{
lock();
}
/**
* @deprecated Use {@link #unlock()} instead
*/
@Deprecated
public void UnLock()
{
unlock();
}
/**
* @deprecated Use {@link #getRenderer()} instead
*/
@Deprecated
public vtkRenderer GetRenderer()
{
return getRenderer();
}
/**
* @deprecated Use {@link #getRenderWindow()} instead
*/
@Deprecated
public vtkRenderWindow GetRenderWindow()
{
return getRenderWindow();
}
public vtkRenderer getRenderer()
{
return ren;
}
public vtkRenderWindow getRenderWindow()
{
return rw;
}
public vtkCamera getCamera()
{
return cam;
}
public vtkLight getLight()
{
return lgt;
}
public vtkRenderWindowInteractor getInteractor()
{
return wi;
}
/**
* return true if currently rendering
*/
public boolean isRendering()
{
return rendering;
}
@Override
public void setBounds(int x, int y, int width, int height)
{
super.setBounds(x, y, width, height);
if (windowset)
{
final int[] size = rw.GetSize();
// set size only if needed
if ((size[0] != width) || (size[1] != height))
{
lock();
try
{
wi.SetSize(width, height);
rw.SetSize(width, height);
sizeChanged();
}
finally
{
unlock();
}
}
}
}
/**
* Called when window render size changed (helper for this specific event)
*/
public void sizeChanged()
{
// nothing here but can be overridden
}
/**
* @deprecated Use {@link #render()} instead.
*/
@Deprecated
public void Render()
{
render();
}
/**
* Do rendering
*/
public void render()
{
if (rendering)
return;
rendering = true;
lock();
try
{
rw.Render();
}
finally
{
unlock();
rendering = false;
}
}
// public synchronized void Render()
// {
// // already rendering or rendering windows not defined --> exit
// if ((rendering) || (rw == null))
// return;
// // nothing to do --> exit
// if (ren.VisibleActorCount() == 0)
// return;
//
// rendering = true;
//
// try
// {
// if (windowset == 0)
// {
// // set the window id and the active camera
// cam = ren.GetActiveCamera();
//
// if (lightingset == 0)
// {
// ren.AddLight(lgt);
// lgt.SetPosition(cam.GetPosition());
// lgt.SetFocalPoint(cam.GetFocalPoint());
// lightingset = 1;
// }
//
// windowset = 1;
// setSize(getWidth(), getHeight());
// }
//
// lock();
// rw.Render();
// unlock();
// }
// finally
// {
// rendering = false;
// }
// }
public boolean isWindowSet()
{
return windowset;
}
public void lock()
{
lock.lock();
}
public void unlock()
{
lock.unlock();
}
/**
* @deprecated do nothing now
*/
@Deprecated
public void InteractionModeRotate()
{
//
}
/**
* @deprecated do nothing now
*/
@Deprecated
public void InteractionModeTranslate()
{
//
}
/**
* @deprecated do nothing now
*/
@Deprecated
public void InteractionModeZoom()
{
//
}
/**
* @deprecated Use {@link #updateLight()} instead
*/
@Deprecated
public void UpdateLight()
{
updateLight();
}
public void updateLight()
{
lgt.SetPosition(cam.GetPosition());
lgt.SetFocalPoint(cam.GetFocalPoint());
}
public void resetCameraClippingRange()
{
lock();
try
{
ren.ResetCameraClippingRange();
}
finally
{
unlock();
}
}
public void resetCamera()
{
lock();
try
{
ren.ResetCamera();
}
finally
{
unlock();
}
}
@Override
public void paint(Graphics g)
{
// previous failed --> do nothing now
if (failed)
return;
try
{
super.paint(g);
}
catch (Throwable t)
{
// it can happen with older video cards
failed = true;
new FailedAnnounceFrame("An error occured while initializing OpenGL !\n"
+ "You may try to update your graphics card driver to fix this issue.", 0);
IcyExceptionHandler.handleException(t, true);
}
}
/**
* @deprecated Use {@link #doHardCopy(String, int)} instead
*/
@Deprecated
public void HardCopy(String filename, int mag)
{
doHardCopy(filename, mag);
}
public void doHardCopy(String filename, int mag)
{
lock();
vtkWindowToImageFilter w2if = new vtkWindowToImageFilter();
w2if.SetInput(rw);
w2if.SetMagnification(mag);
w2if.Update();
vtkTIFFWriter writer = new vtkTIFFWriter();
writer.SetInputConnection(w2if.GetOutputPort());
writer.SetFileName(filename);
writer.Write();
unlock();
}
}