001package icy.vtk;
002
003import icy.gui.dialog.IdConfirmDialog;
004import icy.gui.frame.progress.FailedAnnounceFrame;
005import icy.system.IcyExceptionHandler;
006import icy.system.IcyHandledException;
007import icy.system.thread.ThreadUtil;
008import icy.util.OpenGLUtil;
009import icy.util.ReflectionUtil;
010
011import java.awt.Graphics;
012import java.util.concurrent.locks.ReentrantLock;
013
014import javax.media.opengl.GLAutoDrawable;
015import javax.media.opengl.GLCapabilities;
016import javax.media.opengl.GLContext;
017import javax.media.opengl.GLEventListener;
018import javax.media.opengl.GLProfile;
019import javax.media.opengl.awt.GLJPanel;
020
021import jogamp.opengl.GLDrawableHelper;
022import vtk.vtkCamera;
023import vtk.vtkGenericOpenGLRenderWindow;
024import vtk.vtkGenericRenderWindowInteractor;
025import vtk.vtkLight;
026import vtk.vtkRenderWindow;
027import vtk.vtkRenderWindowInteractor;
028import vtk.vtkRenderer;
029import vtk.vtkTIFFWriter;
030import vtk.vtkWindowToImageFilter;
031
032public class VtkJoglPanel extends GLJPanel
033{
034    class GLEventImpl implements GLEventListener
035    {
036        @Override
037        public void init(GLAutoDrawable drawable)
038        {
039            if (!windowset)
040            {
041                windowset = true;
042
043                // Make sure the JOGL Context is current
044                GLContext ctx = drawable.getContext();
045                if (!ctx.isCurrent())
046                    ctx.makeCurrent();
047
048                // Init VTK OpenGL RenderWindow
049                rw.SetMapped(1);
050                rw.SetPosition(0, 0);
051                setSize(drawable.getWidth(), drawable.getHeight());
052                rw.OpenGLInit();
053
054                // init light
055                if (!lightingset)
056                {
057                    lightingset = true;
058                    ren.AddLight(lgt);
059                    lgt.SetPosition(cam.GetPosition());
060                    lgt.SetFocalPoint(cam.GetFocalPoint());
061                }
062            }
063        }
064
065        @Override
066        public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height)
067        {
068            setSize(width, height);
069        }
070
071        @Override
072        public void display(GLAutoDrawable drawable)
073        {
074            render();
075        }
076
077        @Override
078        public void dispose(GLAutoDrawable drawable)
079        {
080            delete();
081        }
082    }
083
084    /**
085     * 
086     */
087    private static final long serialVersionUID = 8821516677188995191L;
088
089    protected vtkGenericOpenGLRenderWindow rw;
090    protected vtkRenderer ren;
091    protected vtkRenderWindowInteractor wi;
092    protected vtkCamera cam;
093    protected vtkLight lgt;
094
095    protected ReentrantLock lock;
096    protected GLEventImpl glEventImpl;
097
098    protected int lastX;
099    protected int lastY;
100    protected boolean windowset;
101    protected boolean lightingset;
102    protected int interactionMode;
103    protected boolean rendering;
104    private boolean failed;
105
106    public VtkJoglPanel()
107    {
108        super(new GLCapabilities(GLProfile.getDefault()));
109
110        rw = new vtkGenericOpenGLRenderWindow();
111
112        // init render window
113        rw.SetIsDirect(1);
114        rw.SetSupportsOpenGL(1);
115        rw.SetIsCurrent(true);
116
117        // FIXME: smoothing is broken with VTK 6.3
118        // rw.SetPointSmoothing(1);
119        // rw.SetLineSmoothing(1);
120        // rw.SetPolygonSmoothing(1);
121        // rw.SetMultiSamples(4);
122
123        // init window interactor
124        wi = new vtkGenericRenderWindowInteractor();
125        wi.SetRenderWindow(rw);
126        wi.ConfigureEvent();
127
128        ren = new vtkRenderer();
129        ren.SetLightFollowCamera(1);
130
131        cam = null;
132
133        lgt = new vtkLight();
134        // set ambient color to white
135        lgt.SetAmbientColor(1d, 1d, 1d);
136
137        lock = new ReentrantLock();
138        glEventImpl = new GLEventImpl();
139
140        windowset = false;
141        lightingset = false;
142        rendering = false;
143        failed = false;
144
145        addGLEventListener(glEventImpl);
146
147        rw.AddRenderer(ren);
148        cam = ren.GetActiveCamera();
149
150        // super.setSize(200, 200);
151        // rw.SetSize(200, 200);
152
153        // not compatible with OpenGL 3 ? (new VTK OpenGL backend require OpenGL 3.2)
154        if (!OpenGLUtil.isOpenGLSupported(3))
155        {
156            if (!IdConfirmDialog
157                    .confirm(
158                            "Warning",
159                            "Your graphics card driver does not support OpenGL 3, you may experience issues or crashes with VTK.\nDo you want to try anyway ?",
160                            IdConfirmDialog.YES_NO_OPTION, getClass().getName() + ".notCompatibleDialog"))
161                throw new IcyHandledException("Your graphics card driver is not compatible with OpenGL 3 !");
162        }
163    }
164
165    /**
166     * @deprecated Use {@link #disposeInternal()} instead
167     */
168    @Deprecated
169    public void Delete()
170    {
171        delete();
172    }
173
174    protected void delete()
175    {
176        if (rendering)
177        {
178            rw.SetAbortRender(1);
179            // wait a bit while rendering
180            ThreadUtil.sleep(500);
181            // still rendering --> exit
182            if (rendering)
183                return;
184        }
185
186        lock.lock();
187        try
188        {
189            // prevent any further rendering
190            rendering = true;
191
192            // if (getParent() != null)
193            // getParent().remove(this);
194
195            // release internal VTK objects
196            ren = null;
197            cam = null;
198            lgt = null;
199
200            // On linux we prefer to have a memory leak instead of a crash
201            if (!rw.GetClassName().equals("vtkXOpenGLRenderWindow"))
202            {
203                rw = null;
204            }
205            else
206            {
207                System.out.println("The renderwindow has been kept arount to prevent a crash");
208            }
209
210            // call it only once in parent as this can take a lot of time
211            // vtkObjectBase.JAVA_OBJECT_MANAGER.gc(false);
212        }
213        finally
214        {
215            // removing the renderWindow is let to the superclass
216            // because in the very special case of an AWT component
217            // under Linux, destroying renderWindow crashes.
218            lock.unlock();
219        }
220    }
221
222    /**
223     * Disable method, use {@link #disposeInternal()} instead to release VTK and OpenGL resources
224     */
225    @Override
226    protected void dispose()
227    {
228        // prevent disposal on removeNotify as window externalization produce remove/add operation.
229        // --> don't forget to call disposeInternal when needed
230    }
231
232    /**
233     * Release VTK and OGL objects.<br>
234     * Call it when you know you won't use anymore the VTK OGL panel
235     */
236    public void disposeInternal()
237    {
238        super.dispose();
239
240        // remove the GL event listener to avoid memory leak
241        removeGLEventListener(glEventImpl);
242
243        try
244        {
245            // hacky fix to avoid the infamous memory leak from ThreadLocal from GLPanel !
246            final GLDrawableHelper helper = (GLDrawableHelper) ReflectionUtil.getFieldObject(this, "helper", true);
247            final ThreadLocal threadLocal = (ThreadLocal) ReflectionUtil.getFieldObject(helper, "perThreadInitAction",
248                    true);
249            threadLocal.remove();
250        }
251        catch (Throwable t)
252        {
253            // ignore
254        }
255    }
256
257    /**
258     * @deprecated Use {@link #lock()} instead
259     */
260    @Deprecated
261    public void Lock()
262    {
263        lock();
264    }
265
266    /**
267     * @deprecated Use {@link #unlock()} instead
268     */
269    @Deprecated
270    public void UnLock()
271    {
272        unlock();
273    }
274
275    /**
276     * @deprecated Use {@link #getRenderer()} instead
277     */
278    @Deprecated
279    public vtkRenderer GetRenderer()
280    {
281        return getRenderer();
282    }
283
284    /**
285     * @deprecated Use {@link #getRenderWindow()} instead
286     */
287    @Deprecated
288    public vtkRenderWindow GetRenderWindow()
289    {
290        return getRenderWindow();
291    }
292
293    public vtkRenderer getRenderer()
294    {
295        return ren;
296    }
297
298    public vtkRenderWindow getRenderWindow()
299    {
300        return rw;
301    }
302
303    public vtkCamera getCamera()
304    {
305        return cam;
306    }
307
308    public vtkLight getLight()
309    {
310        return lgt;
311    }
312
313    public vtkRenderWindowInteractor getInteractor()
314    {
315        return wi;
316    }
317
318    /**
319     * return true if currently rendering
320     */
321    public boolean isRendering()
322    {
323        return rendering;
324    }
325
326    @Override
327    public void setBounds(int x, int y, int width, int height)
328    {
329        super.setBounds(x, y, width, height);
330
331        if (windowset)
332        {
333            final int[] size = rw.GetSize();
334
335            // set size only if needed
336            if ((size[0] != width) || (size[1] != height))
337            {
338                lock();
339                try
340                {
341                    wi.SetSize(width, height);
342                    rw.SetSize(width, height);
343                    sizeChanged();
344                }
345                finally
346                {
347                    unlock();
348                }
349            }
350        }
351    }
352
353    /**
354     * Called when window render size changed (helper for this specific event)
355     */
356    public void sizeChanged()
357    {
358        // nothing here but can be overridden
359    }
360
361    /**
362     * @deprecated Use {@link #render()} instead.
363     */
364    @Deprecated
365    public void Render()
366    {
367        render();
368    }
369
370    /**
371     * Do rendering
372     */
373    public void render()
374    {
375        if (rendering)
376            return;
377
378        rendering = true;
379        lock();
380        try
381        {
382            rw.Render();
383        }
384        finally
385        {
386            unlock();
387            rendering = false;
388        }
389    }
390
391    // public synchronized void Render()
392    // {
393    // // already rendering or rendering windows not defined --> exit
394    // if ((rendering) || (rw == null))
395    // return;
396    // // nothing to do --> exit
397    // if (ren.VisibleActorCount() == 0)
398    // return;
399    //
400    // rendering = true;
401    //
402    // try
403    // {
404    // if (windowset == 0)
405    // {
406    // // set the window id and the active camera
407    // cam = ren.GetActiveCamera();
408    //
409    // if (lightingset == 0)
410    // {
411    // ren.AddLight(lgt);
412    // lgt.SetPosition(cam.GetPosition());
413    // lgt.SetFocalPoint(cam.GetFocalPoint());
414    // lightingset = 1;
415    // }
416    //
417    // windowset = 1;
418    // setSize(getWidth(), getHeight());
419    // }
420    //
421    // lock();
422    // rw.Render();
423    // unlock();
424    // }
425    // finally
426    // {
427    // rendering = false;
428    // }
429    // }
430
431    public boolean isWindowSet()
432    {
433        return windowset;
434    }
435
436    public void lock()
437    {
438        lock.lock();
439    }
440
441    public void unlock()
442    {
443        lock.unlock();
444    }
445
446    /**
447     * @deprecated do nothing now
448     */
449    @Deprecated
450    public void InteractionModeRotate()
451    {
452        //
453    }
454
455    /**
456     * @deprecated do nothing now
457     */
458    @Deprecated
459    public void InteractionModeTranslate()
460    {
461        //
462    }
463
464    /**
465     * @deprecated do nothing now
466     */
467    @Deprecated
468    public void InteractionModeZoom()
469    {
470        //
471    }
472
473    /**
474     * @deprecated Use {@link #updateLight()} instead
475     */
476    @Deprecated
477    public void UpdateLight()
478    {
479        updateLight();
480    }
481
482    public void updateLight()
483    {
484        lgt.SetPosition(cam.GetPosition());
485        lgt.SetFocalPoint(cam.GetFocalPoint());
486    }
487
488    public void resetCameraClippingRange()
489    {
490        lock();
491        try
492        {
493            ren.ResetCameraClippingRange();
494        }
495        finally
496        {
497            unlock();
498        }
499    }
500
501    public void resetCamera()
502    {
503        lock();
504        try
505        {
506            ren.ResetCamera();
507        }
508        finally
509        {
510            unlock();
511        }
512    }
513
514    @Override
515    public void paint(Graphics g)
516    {
517        // previous failed --> do nothing now
518        if (failed)
519            return;
520
521        try
522        {
523            super.paint(g);
524        }
525        catch (Throwable t)
526        {
527            // it can happen with older video cards
528            failed = true;
529
530            new FailedAnnounceFrame("An error occured while initializing OpenGL !\n"
531                            + "You may try to update your graphics card driver to fix this issue.", 0);
532
533            IcyExceptionHandler.handleException(t, true);
534        }
535    }
536
537    /**
538     * @deprecated Use {@link #doHardCopy(String, int)} instead
539     */
540    @Deprecated
541    public void HardCopy(String filename, int mag)
542    {
543        doHardCopy(filename, mag);
544    }
545
546    public void doHardCopy(String filename, int mag)
547    {
548        lock();
549
550        vtkWindowToImageFilter w2if = new vtkWindowToImageFilter();
551        w2if.SetInput(rw);
552
553        w2if.SetMagnification(mag);
554        w2if.Update();
555
556        vtkTIFFWriter writer = new vtkTIFFWriter();
557        writer.SetInputConnection(w2if.GetOutputPort());
558        writer.SetFileName(filename);
559        writer.Write();
560
561        unlock();
562    }
563}