Adds possibility to color marks in scatter plots
[scilab.git] / scilab / modules / scirenderer / src / org / scilab / forge / scirenderer / implementation / jogl / drawer / JoGLShapeDrawer.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.drawer;
13
14 import org.scilab.forge.scirenderer.SciRendererException;
15 import org.scilab.forge.scirenderer.buffers.IndicesBuffer;
16 import org.scilab.forge.scirenderer.implementation.jogl.JoGLDrawingTools;
17 import org.scilab.forge.scirenderer.implementation.jogl.buffers.JoGLBuffersManager;
18 import org.scilab.forge.scirenderer.implementation.jogl.buffers.JoGLElementsBuffer;
19 import org.scilab.forge.scirenderer.implementation.jogl.utils.GLShortCuts;
20 import org.scilab.forge.scirenderer.shapes.appearance.Appearance;
21 import org.scilab.forge.scirenderer.shapes.geometry.Geometry;
22 import org.scilab.forge.scirenderer.texture.Texture;
23 import org.scilab.forge.scirenderer.lightning.LightManager;
24 import org.scilab.forge.scirenderer.shapes.appearance.Material;
25
26 import javax.media.opengl.GL2;
27 import java.nio.FloatBuffer;
28 import java.nio.IntBuffer;
29
30 /**
31  * Utility class for drawing shapes.
32  * @author Pierre Lando
33  */
34 public final class JoGLShapeDrawer {
35
36     private static JoGLShapeDrawer drawer;
37
38     /**
39      * Private constructor : this is an utility class.
40      */
41     private JoGLShapeDrawer() {
42     }
43
44     /**
45      * Singleton getter.
46      * @return the unique {@link JoGLShapeDrawer}.
47      */
48     public static JoGLShapeDrawer getDrawer() {
49         if (drawer == null) {
50             drawer = new JoGLShapeDrawer();
51         }
52         return drawer;
53     }
54
55     /**
56      * Draw a given geometry with given appearance.
57      * @param drawingTools the drawing tools.
58      * @param geometry the geometry.
59      * @param appearance the appearance.
60      * @throws org.scilab.forge.scirenderer.SciRendererException if the draw is not possible.
61      */
62     public void draw(JoGLDrawingTools drawingTools, Geometry geometry, Appearance appearance) throws SciRendererException {
63         GL2 gl = drawingTools.getGl().getGL2();
64         gl.glFrontFace(GL2.GL_CCW);
65         switch (geometry.getFaceCullingMode()) {
66             case CW:
67                 gl.glEnable(GL2.GL_CULL_FACE);
68                 gl.glCullFace(GL2.GL_FRONT);
69                 break;
70             case CCW:
71                 gl.glEnable(GL2.GL_CULL_FACE);
72                 gl.glCullFace(GL2.GL_BACK);
73                 break;
74             case BOTH:
75                 gl.glDisable(GL2.GL_CULL_FACE);
76                 break;
77             default:
78                 gl.glDisable(GL2.GL_CULL_FACE);
79                 break;
80         }
81
82         if (drawingTools.getCanvas().getJoGLParameters().useVBO()) {
83             vboDrawing(drawingTools, geometry, appearance);
84         } else {
85             directDrawing(drawingTools, geometry, appearance);
86         }
87
88         GLShortCuts.useLineAppearance(gl, null);
89         gl.glDisable(GL2.GL_CULL_FACE);
90     }
91
92     /**
93      * Perform geometry drawing using VBO.
94      * @param drawingTools the drawing tools.
95      * @param geometry the geometry to draw.
96      * @param appearance the current appearance.
97      * @throws org.scilab.forge.scirenderer.SciRendererException if the draw is not possible.
98      */
99     private void vboDrawing(JoGLDrawingTools drawingTools, Geometry geometry, Appearance appearance) throws SciRendererException {
100         final GL2 gl = drawingTools.getGl().getGL2();
101         final JoGLBuffersManager buffersManager = drawingTools.getCanvas().getBuffersManager();
102         final Texture texture = appearance.getTexture();
103
104         int verticesNumber = buffersManager.bindVertexBuffer(gl, geometry.getVertices());
105         if (verticesNumber == 0) {
106             gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
107             return;
108         }
109
110         buffersManager.bindNormalsBuffer(gl, geometry.getNormals());
111
112         if (texture != null && geometry.getTextureCoordinates() != null) {
113             synchronized (texture) {
114                 if (texture.isValid()) {
115                     drawingTools.bind(texture);
116                     buffersManager.bindTextureCoordinatesBuffer(gl, geometry.getTextureCoordinates());
117                 }
118             }
119         } else {
120             buffersManager.bindColorsBuffer(gl, geometry.getColors());
121         }
122
123         // We use polygon offset for filled geometry if required.
124         if (geometry.getPolygonOffsetMode()) {
125             gl.glEnable(GL2.GL_POLYGON_OFFSET_FILL);
126             gl.glPolygonOffset(1, 1);
127         }
128
129         GLShortCuts.useColor(gl, appearance.getFillColor());
130
131         LightManager lm = drawingTools.getLightManager();
132         boolean lighting = lm.isLightningEnable();
133         if (lighting) {
134             lm.setMaterial(appearance.getMaterial());
135             gl.glEnable(GL2.GL_NORMALIZE);
136         }
137
138         IndicesBuffer indices = geometry.getIndices();
139         if (geometry.getFillDrawingMode() != Geometry.FillDrawingMode.NONE) {
140             if (indices != null) {
141                 int indicesSize = buffersManager.bindIndicesBuffer(gl, indices);
142                 if (indicesSize > 0) {
143                     gl.glDrawElements(getGlMode(geometry.getFillDrawingMode()), indicesSize, GL2.GL_UNSIGNED_INT, 0);
144                     gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0);
145                 }
146             } else {
147                 int count = geometry.getVertices().getSize();
148                 if (count > 0) {
149                     gl.glDrawArrays(getGlMode(geometry.getFillDrawingMode()), 0, count);
150                 }
151             }
152         }
153
154         if (geometry.getPolygonOffsetMode()) {
155             gl.glDisable(GL2.GL_POLYGON_OFFSET_FILL);
156         }
157
158         gl.glDisableClientState(GL2.GL_COLOR_ARRAY);
159         gl.glDisableClientState(GL2.GL_NORMAL_ARRAY);
160         gl.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY);
161         gl.glDisable(GL2.GL_TEXTURE_2D);
162         //disable lighting to draw lines
163         lm.setLightningEnable(false);
164         gl.glDisable(GL2.GL_NORMALIZE);
165
166         if (geometry.getLineDrawingMode() != Geometry.LineDrawingMode.NONE) {
167             // to avoid color smoothing between edge
168             // TODO: add an option in Appearance
169             gl.glShadeModel(GL2.GL_FLAT);
170             if (appearance.getLineColor() != null || geometry.getColors() != null) {
171                 GLShortCuts.useLineAppearance(gl, appearance);
172                 if (appearance.getLineColor() == null) {
173                     buffersManager.bindColorsBuffer(gl, geometry.getColors());
174                 }
175
176                 if (geometry.getWireIndices() != null) {
177                     int edgesIndicesSize = buffersManager.bindIndicesBuffer(gl, geometry.getWireIndices());
178                     if (edgesIndicesSize > 0) {
179                         gl.glDrawElements(getGlMode(geometry.getLineDrawingMode()), edgesIndicesSize, GL2.GL_UNSIGNED_INT, 0);
180                         gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0);
181                     }
182                 } else {
183                     int count = geometry.getVertices().getSize();
184                     if (count > 0) {
185                         gl.glDrawArrays(getGlMode(geometry.getLineDrawingMode()), 0, count);
186                     }
187                 }
188
189                 gl.glDisableClientState(GL2.GL_COLOR_ARRAY);
190             }
191         }
192
193         gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
194         lm.setLightningEnable(lighting);
195     }
196
197     /**
198      * Perform geometry drawing by direct OpenGl call.
199      * @param drawingTools the drawing tools.
200      * @param geometry the geometry to draw.
201      * @param appearance the used appearance.
202      * @throws org.scilab.forge.scirenderer.SciRendererException if the draw is not possible.
203      */
204     private void directDrawing(
205         JoGLDrawingTools drawingTools, Geometry geometry, Appearance appearance
206     ) throws SciRendererException {
207         final double sfactor;
208         final double tfactor;
209         final float[] fbuffer = new float[4];
210
211         GL2 gl = drawingTools.getGl().getGL2();
212         if (geometry.getVertices() == null) {
213             return;
214         }
215
216         FloatBuffer vertexBuffer = geometry.getVertices().getData();
217         IndicesBuffer indices = geometry.getIndices();
218
219         FloatBuffer colorBuffer;
220         if (geometry.getColors() != null) {
221             colorBuffer = geometry.getColors().getData();
222         } else {
223             colorBuffer = null;
224         }
225
226         FloatBuffer normalBuffer;
227         if (geometry.getNormals() != null) {
228             normalBuffer = geometry.getNormals().getData();
229         } else {
230             normalBuffer = null;
231         }
232
233         Texture texture = appearance.getTexture();
234         FloatBuffer textureCoordinatesBuffer;
235         if (texture != null && geometry.getTextureCoordinates() != null) {
236             synchronized (texture) {
237                 drawingTools.bind(texture);
238                 textureCoordinatesBuffer = geometry.getTextureCoordinates().getData();
239                 sfactor = texture.getSScaleFactor();
240                 tfactor = texture.getTScaleFactor();
241             }
242         } else {
243             textureCoordinatesBuffer = null;
244             sfactor = 1;
245             tfactor = 1;
246         }
247
248         final int elementsSize = JoGLElementsBuffer.ELEMENT_SIZE;
249
250         if (geometry.getPolygonOffsetMode()) {
251             gl.glEnable(GL2.GL_POLYGON_OFFSET_FILL);
252             gl.glPolygonOffset(1, 1);
253         }
254
255         LightManager lm = drawingTools.getLightManager();
256         boolean lighting = lm.isLightningEnable();
257         if (lighting) {
258             lm.setMaterial(appearance.getMaterial());
259         }
260
261         if (geometry.getFillDrawingMode() != Geometry.FillDrawingMode.NONE) {
262             GLShortCuts.useColor(gl, appearance.getFillColor());
263             gl.glShadeModel(GL2.GL_FLAT);
264             gl.glBegin(getGlMode(geometry.getFillDrawingMode()));
265             if (indices != null) {
266                 IntBuffer indicesBuffer = indices.getData();
267                 indicesBuffer.rewind();
268                 for (int i = 0; i < indicesBuffer.limit(); i++) {
269                     int index = indicesBuffer.get(i);
270                     if ((index * elementsSize) < vertexBuffer.limit()) {
271
272                         if (colorBuffer != null) {
273                             colorBuffer.position(index * elementsSize);
274                             gl.glColor4fv(colorBuffer);
275                         }
276
277                         if (normalBuffer != null) {
278                             normalBuffer.position(index * elementsSize);
279                             gl.glNormal3fv(normalBuffer);
280                         }
281
282                         if (textureCoordinatesBuffer != null) {
283                             textureCoordinatesBuffer.position(index * elementsSize);
284                             textureCoordinatesBuffer.get(fbuffer);
285
286                             gl.glTexCoord4f((float) (sfactor * fbuffer[0]), (float) (tfactor * fbuffer[1]), fbuffer[2], fbuffer[3]);
287                         }
288
289                         vertexBuffer.position(index * elementsSize);
290                         gl.glVertex4fv(vertexBuffer);
291
292                     }
293                 }
294             } else {
295                 vertexBuffer.rewind();
296
297                 if (colorBuffer != null) {
298                     colorBuffer.rewind();
299                 }
300
301                 if (normalBuffer != null) {
302                     normalBuffer.rewind();
303                 }
304
305                 for (int i = 0; i < vertexBuffer.limit(); i += elementsSize) {
306                     if (colorBuffer != null) {
307                         colorBuffer.position(i);
308                         gl.glColor4fv(colorBuffer);
309                     }
310
311                     if (normalBuffer != null) {
312                         normalBuffer.position(i);
313                         gl.glNormal3fv(normalBuffer);
314                     }
315
316                     if (textureCoordinatesBuffer != null) {
317                         textureCoordinatesBuffer.position(i);
318                         gl.glTexCoord4fv(textureCoordinatesBuffer);
319                     }
320
321                     vertexBuffer.position(i);
322                     gl.glVertex4fv(vertexBuffer);
323                 }
324             }
325             gl.glEnd();
326         }
327
328         if (geometry.getPolygonOffsetMode()) {
329             gl.glDisable(GL2.GL_POLYGON_OFFSET_FILL);
330         }
331
332         gl.glDisable(GL2.GL_TEXTURE_2D);
333         //disable lighting to draw lines
334         lm.setLightningEnable(false);
335
336         // Draw edges if any.
337         if (geometry.getLineDrawingMode() != Geometry.LineDrawingMode.NONE) {
338             GLShortCuts.useLineAppearance(gl, appearance);
339             if (appearance.getLineColor() != null) {
340                 gl.glBegin(getGlMode(geometry.getLineDrawingMode()));
341                 if (geometry.getWireIndices() != null) {
342                     IntBuffer edgesIndicesBuffer = geometry.getWireIndices().getData();
343                     edgesIndicesBuffer.rewind();
344                     while (edgesIndicesBuffer.remaining() != 0) {
345                         int index = edgesIndicesBuffer.get();
346                         if ((index * elementsSize) < vertexBuffer.limit()) {
347                             vertexBuffer.position(index * elementsSize);
348                             gl.glVertex4fv(vertexBuffer);
349                         }
350                     }
351                 } else {
352                     for (int i = 0; i < vertexBuffer.limit(); i += elementsSize) {
353                         vertexBuffer.position(i);
354                         gl.glVertex4fv(vertexBuffer);
355                     }
356                 }
357                 gl.glEnd();
358             } else if (colorBuffer != null) {
359                 gl.glBegin(getGlMode(geometry.getLineDrawingMode()));
360                 if (geometry.getWireIndices() != null) {
361                     IntBuffer edgesIndicesBuffer = geometry.getWireIndices().getData();
362                     edgesIndicesBuffer.rewind();
363                     while (edgesIndicesBuffer.remaining() != 0) {
364                         int index = edgesIndicesBuffer.get();
365                         if ((index * elementsSize) < vertexBuffer.limit()) {
366                             colorBuffer.position(index * elementsSize);
367                             gl.glColor4fv(colorBuffer);
368
369                             vertexBuffer.position(index * elementsSize);
370                             gl.glVertex4fv(vertexBuffer);
371
372                         }
373                     }
374                 } else {
375                     for (int i = 0; i < vertexBuffer.limit(); i += elementsSize) {
376                         colorBuffer.position(i);
377                         vertexBuffer.position(i);
378                         gl.glColor4fv(colorBuffer);
379                         gl.glVertex4fv(vertexBuffer);
380                     }
381                 }
382                 gl.glEnd();
383             }
384         }
385         lm.setLightningEnable(lighting);
386     }
387
388
389     /**
390      * Return the gl drawing mode corresponding to the given {@link Geometry.FillDrawingMode}.
391      * @param drawingMode the given drawing mode..
392      * @return the gl drawing mode corresponding to the given {@link Geometry.FillDrawingMode}.
393      */
394     private int getGlMode(Geometry.FillDrawingMode drawingMode) {
395         switch (drawingMode) {
396             case TRIANGLE_FAN:
397                 return GL2.GL_TRIANGLE_FAN;
398             case TRIANGLE_STRIP:
399                 return GL2.GL_TRIANGLE_STRIP;
400             case TRIANGLES:
401                 return GL2.GL_TRIANGLES;
402             default:
403                 return GL2.GL_TRIANGLES;
404         }
405     }
406
407     /**
408      * Return the gl drawing mode corresponding to the given {@link org.scilab.forge.scirenderer.shapes.geometry.Geometry.LineDrawingMode}
409      * @param drawingMode the given drawing mode.
410      * @return the gl drawing mode corresponding to the given {@link org.scilab.forge.scirenderer.shapes.geometry.Geometry.LineDrawingMode}
411      */
412     private int getGlMode(Geometry.LineDrawingMode drawingMode) {
413         switch (drawingMode) {
414             case SEGMENTS:
415                 return GL2.GL_LINES;
416             case SEGMENTS_LOOP:
417                 return GL2.GL_LINE_LOOP;
418             case SEGMENTS_STRIP:
419                 return GL2.GL_LINE_STRIP;
420             default:
421                 return GL2.GL_LINES;
422         }
423     }
424 }