96c0f92db2c90f178efb6b17b36b1b1594f5185e
[scilab.git] / scilab / modules / scirenderer / src / org / scilab / forge / scirenderer / implementation / jogl / JoGLCanvas.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009-2011 - DIGITEO - Pierre Lando
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  */
11
12 package org.scilab.forge.scirenderer.implementation.jogl;
13
14 import org.scilab.forge.scirenderer.Canvas;
15 import org.scilab.forge.scirenderer.Drawer;
16 import org.scilab.forge.scirenderer.implementation.jogl.buffers.JoGLBuffersManager;
17 import org.scilab.forge.scirenderer.implementation.jogl.picking.JoGLPickingManager;
18 import org.scilab.forge.scirenderer.implementation.jogl.renderer.JoGLRendererManager;
19 import org.scilab.forge.scirenderer.implementation.jogl.texture.JoGLTextureManager;
20 import org.scilab.forge.scirenderer.picking.PickingManager;
21
22 import com.jogamp.opengl.util.awt.ImageUtil;
23 import com.jogamp.opengl.util.awt.Screenshot;
24 import java.awt.Dimension;
25 import java.awt.image.BufferedImage;
26 import java.lang.reflect.InvocationTargetException;
27 import java.util.concurrent.Semaphore;
28 import javax.media.opengl.DebugGL2;
29 import javax.media.opengl.GL2;
30 import javax.media.opengl.GLAutoDrawable;
31 import javax.media.opengl.GLCapabilities;
32 import javax.media.opengl.GLContext;
33 import javax.media.opengl.GLDrawableFactory;
34 import javax.media.opengl.GLEventListener;
35 import javax.media.opengl.GLException;
36 import javax.media.opengl.GLPbuffer;
37 import javax.media.opengl.GLProfile;
38 import javax.swing.SwingUtilities;
39
40 /**
41  * JoGL implementation of a Canvas.
42  *
43  * @author Pierre Lando
44  */
45 public final class JoGLCanvas implements Canvas, GLEventListener {
46
47     private static final double[][][] ANTI_ALIASING_JITTER = {
48         {{0.0, 0.0}},
49         {{0.5, 0.5}, {0.5, -0.5}},
50         {
51             { -0.25, -0.5}, {0.25, 0.5}, {0.75, -0.5}, {0.25, 0.5}
52         },
53         {
54             {0.125, -0.125}, { -0.875, 0.875}, { -0.375, 0.375}, {0.375, 0.625},
55             {0.625, -0.625}, {0.875, 0.125}, { -0.125, -0.875}, { -0.625, -0.375}
56         },
57         {
58             { -0.25, -0.125}, {0.25, -0.875}, {0.75, -0.625}, { -0.75, -0.875},
59             { -0.25, 0.375}, {0.75, -0.125}, {0.25, 0.125}, { -0.25, 0.875},
60             {0.25, -0.375}, { -0.75, 0.125}, { -0.75, 0.625}, { -0.25, -0.625},
61             {0.75, 0.875}, {0.75, 0.375}, { -0.75, -0.375}, {0.25, 0.625}
62         }
63     };
64
65     private final GLAutoDrawable autoDrawable;
66
67     private final JoGLDrawingTools drawingTools;
68     private final JoGLParameters parameters;
69     private final JoGLBuffersManager buffersManager;
70     private final JoGLRendererManager rendererManager;
71     private final JoGLPickingManager pickingManager;
72     private final JoGLTextureManager textureManager;
73
74     private final CanvasAnimator canvasAnimator;
75     private boolean isOffscreen;
76     private DebugGL2 debug;
77     private boolean isValid = true;
78     private boolean displayFinished;
79
80
81     /** The current mainDrawer. */
82     private Drawer mainDrawer;
83
84     /** The anti-aliasing level */
85     private int antiAliasingLevel = 0;
86
87     /**
88      * Default constructor.
89      * @param autoDrawable the JoGL autoDrawable this canvas depend on.
90      */
91     JoGLCanvas(GLAutoDrawable autoDrawable) {
92         this.autoDrawable = autoDrawable;
93         parameters = new JoGLParameters();
94         buffersManager = new JoGLBuffersManager();
95         rendererManager = new JoGLRendererManager();
96         drawingTools = new JoGLDrawingTools(this);
97         pickingManager = new JoGLPickingManager(this);
98         textureManager = new JoGLTextureManager(this);
99
100         autoDrawable.addGLEventListener(this);
101         canvasAnimator = new CanvasAnimator(autoDrawable);
102         canvasAnimator.redraw();
103     }
104
105     /**
106      * Constructor for offscreen rendering
107      * @param width the width
108      * @param height the height
109      */
110     JoGLCanvas(int width, int height) {
111         this(getOffscreenDrawable(width, height));
112         isOffscreen = true;
113     }
114
115     public void setDebugMode(boolean debug) {
116         if (debug) {
117             this.debug = new DebugGL2(autoDrawable.getGL().getGL2());
118         } else {
119             this.debug = null;
120         }
121     }
122
123     // Implementation of getter & setter from Canvas.
124
125     @Override
126     public void setMainDrawer(Drawer mainDrawer) {
127         this.mainDrawer = mainDrawer;
128     }
129
130     @Override
131     public Drawer getMainDrawer() {
132         return mainDrawer;
133     }
134
135     @Override
136     public JoGLRendererManager getRendererManager() {
137         return rendererManager;
138     }
139
140     @Override
141     public JoGLBuffersManager getBuffersManager() {
142         return buffersManager;
143     }
144
145     @Override
146     public PickingManager getPickingManager() {
147         return pickingManager;
148     }
149
150     @Override
151     public JoGLTextureManager getTextureManager() {
152         return textureManager;
153     }
154
155     @Override
156     public int getWidth() {
157         return autoDrawable.getWidth();
158     }
159
160     @Override
161     public int getHeight() {
162         return autoDrawable.getHeight();
163     }
164
165     @Override
166     public Dimension getDimension() {
167         return new Dimension(autoDrawable.getWidth(), autoDrawable.getHeight());
168     }
169
170     @Override
171     public void redraw() {
172         canvasAnimator.redraw();
173     }
174
175     @Override
176     public void redrawAndWait() {
177         if (SwingUtilities.isEventDispatchThread()) {
178             if (autoDrawable != null) {
179                 autoDrawable.display();
180             }
181             return;
182         }
183         try {
184             SwingUtilities.invokeAndWait(new Runnable() {
185                 public void run() {
186                     autoDrawable.display();
187                 }
188             });
189         } catch (Exception e) { }
190     }
191
192     @Override
193     public void waitImage() {
194         canvasAnimator.waitEndOfDrawing();
195     }
196
197     @Override
198     public int getAntiAliasingLevel() {
199         return antiAliasingLevel;
200     }
201
202     @Override
203     public void setAntiAliasingLevel(int antiAliasingLevel) {
204         this.antiAliasingLevel = antiAliasingLevel;
205     }
206
207     // JoGLCanvas specific getter.
208
209     /**
210      * Return the OpenGl context.
211      * @return the OpenGl context.
212      */
213     public GL2 getGl() {
214         if (debug == null) {
215             return autoDrawable.getGL().getGL2();
216         } else {
217             return debug;
218         }
219     }
220
221     /**
222      * Return the rendering parameters.
223      * @return the rendering parameters.
224      */
225     public JoGLParameters getJoGLParameters() {
226         return parameters;
227     }
228
229     /**
230      * Get an image from the autoDrawable
231      * @return an image
232      */
233     public BufferedImage getImage() {
234         while (!canvasAnimator.isDrawFinished() || !displayFinished) {
235             try {
236                 Thread.sleep(10);
237             } catch (InterruptedException e) {
238                 break;
239             }
240         }
241
242         final BufferedImage[] image = new BufferedImage[1];
243         final GLContext context = autoDrawable.getContext();
244
245         if (SwingUtilities.isEventDispatchThread()) {
246             context.makeCurrent();
247             image[0] = Screenshot.readToBufferedImage(autoDrawable.getWidth(), autoDrawable.getHeight());
248             context.release();
249         } else {
250             try {
251                 SwingUtilities.invokeAndWait(new Runnable() {
252                     public void run() {
253                         context.makeCurrent();
254                         image[0] = Screenshot.readToBufferedImage(autoDrawable.getWidth(), autoDrawable.getHeight());
255                         context.release();
256                     }
257                 });
258             } catch (InterruptedException e) {
259
260             } catch (InvocationTargetException e) {
261                 System.err.println(e);
262                 e.printStackTrace();
263             }
264         }
265
266         return image[0];
267     }
268
269     /**
270      * Destroy the GLPbuffer
271      */
272     public void destroy() {
273         if (isOffscreen) {
274             ((GLPbuffer) autoDrawable).destroy();
275         }
276         try {
277             isValid = false;
278             canvasAnimator.finalize();//Thread.dumpStack();
279         } catch (Throwable e) {
280             // TODO: handle exception
281         }
282     }
283
284     /**
285      * Creates a GLPbuffer for an offscreen rendering
286      * @param width the width
287      * @param height the height
288      * @return a GLPbuffer
289      */
290     private static GLAutoDrawable getOffscreenDrawable(int width, int height) {
291         GLDrawableFactory factory = GLDrawableFactory.getDesktopFactory();
292         GLCapabilities capabilities = new GLCapabilities(GLProfile.getDefault());
293
294         return factory.createGLPbuffer(null, capabilities, null, width, height, null);
295     }
296
297     // Implementation of function from GLEventListener.
298     @Override
299     public void display(GLAutoDrawable glAutoDrawable) {
300         if (isValid) {
301             displayFinished = false;
302             GL2 gl = getGl().getGL2();
303             buffersManager.glSynchronize(gl);
304             rendererManager.glSynchronize(gl);
305             drawingTools.glSynchronize(gl);
306
307             if (mainDrawer != null) {
308                 gl.glEnable(GL2.GL_DEPTH_TEST);
309                 gl.glDepthFunc(GL2.GL_LEQUAL); // Set to less equal to allow last drawn object to be on the top.
310
311                 if ((antiAliasingLevel > 0) && (antiAliasingLevel < 5) && (drawingTools.getGLCapacity().isAccumulationBufferPresent())) {
312                     org.scilab.forge.scirenderer.renderer.Renderer renderer = rendererManager.createRenderer();
313                     renderer.setDrawer(mainDrawer);
314
315                     double[][] jitter = ANTI_ALIASING_JITTER[antiAliasingLevel];
316                     Dimension dimension = getDimension();
317                     gl.glClear(GL2.GL_ACCUM_BUFFER_BIT);
318                     for (double[] aJitter : jitter) {
319                         drawingTools.glSynchronize(gl);
320
321                         gl.glMatrixMode(GL2.GL_PROJECTION);
322                         gl.glLoadIdentity();
323                         gl.glTranslated(aJitter[0] / dimension.getWidth(), aJitter[1] / dimension.getHeight(), 0.);
324
325                         gl.glClear(GL2.GL_DEPTH_BUFFER_BIT);
326                         rendererManager.draw(drawingTools, renderer);
327                         //mainDrawer.draw(drawingTools);
328                         gl.glReadBuffer(GL2.GL_BACK);
329                         gl.glAccum(GL2.GL_ACCUM, 1f / jitter.length);
330                     }
331
332                     rendererManager.dispose(drawingTools, renderer);
333
334                     gl.glDrawBuffer(GL2.GL_BACK);
335                     gl.glAccum(GL2.GL_RETURN, 1.0f);
336                 } else {
337                     gl.glMatrixMode(GL2.GL_PROJECTION);
338                     gl.glLoadIdentity();
339                     gl.glClear(GL2.GL_DEPTH_BUFFER_BIT);
340                     mainDrawer.draw(drawingTools);
341                 }
342             }
343
344             pickingManager.glConsume(drawingTools);
345             displayFinished = true;
346         }
347     }
348
349     @Override
350     public void init(GLAutoDrawable glAutoDrawable) {
351         textureManager.glReload();
352         buffersManager.glReload();
353         rendererManager.glReload();
354     }
355
356     @Override
357     public void reshape(GLAutoDrawable glAutoDrawable, int x, int y, int width, int height) {
358     }
359
360     @Override
361     public void dispose(GLAutoDrawable drawable) { }
362
363     /**
364      * this class manage asynchronous scene drawing.
365      */
366     private class CanvasAnimator implements Runnable {
367
368         private final Semaphore semaphore = new Semaphore(1);
369         private final Thread thread;
370         private final GLAutoDrawable autoDrawable;
371         private boolean running = true;
372
373         public CanvasAnimator(GLAutoDrawable autoDrawable) {
374             this.autoDrawable = autoDrawable;
375             this.thread = new Thread(this);
376             thread.start();
377             //System.err.println("[DEBUG] nb threads = "+Thread.activeCount());
378         }
379
380         @Override
381         public void finalize() throws Throwable {
382             running = false;
383             // we increment the semaphore to allow run() to unlock it and to be sure
384             // to go out.
385             semaphore.release();
386             autoDrawable.destroy();
387             super.finalize();
388         }
389
390         public synchronized boolean isDrawFinished() {
391             return semaphore.availablePermits() == 0;
392         }
393
394         /** Ask the animator to perform a draw later. */
395         public synchronized void redraw() {
396             semaphore.release();
397         }
398
399         /** Wait until a drawing has been performed */
400         public void waitEndOfDrawing() {
401             semaphore.drainPermits();
402         }
403
404         @Override
405         public void run() {
406             while (running) {
407                 try {
408                     semaphore.acquire();
409                     semaphore.drainPermits();
410                     if (running) {
411                         autoDrawable.display();
412                     }
413                 } catch (InterruptedException e) {
414                     if (running) {
415                         Thread.currentThread().interrupt();
416                     }
417                     break;
418                 } catch (GLException e) {
419                     if (running) {
420                         throw e;
421                     }
422                     break;
423                 }
424             }
425         }
426     }
427 }