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}