75cbc11114f05d57e1ef418a5d650e3ac6859211
[scilab.git] / scilab / modules / scirenderer / src / org / scilab / forge / scirenderer / implementation / g2d / motor / Motor3D.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2012 - Scilab Enterprises - Calixte Denizet
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.g2d.motor;
13
14 import java.awt.Color;
15 import java.awt.Dimension;
16 import java.awt.Graphics2D;
17 import java.awt.RenderingHints;
18 import java.awt.Stroke;
19 import java.awt.image.BufferedImage;
20 import java.nio.FloatBuffer;
21 import java.nio.IntBuffer;
22 import java.util.Arrays;
23 import java.util.List;
24
25 import org.scilab.forge.scirenderer.DrawingTools;
26 import org.scilab.forge.scirenderer.buffers.ElementsBuffer;
27 import org.scilab.forge.scirenderer.clipping.ClippingPlane;
28 import org.scilab.forge.scirenderer.implementation.g2d.G2DCanvas;
29 import org.scilab.forge.scirenderer.implementation.g2d.buffers.G2DElementsBuffer;
30 import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureDrawingTools;
31 import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureManager;
32 import org.scilab.forge.scirenderer.shapes.appearance.Appearance;
33 import org.scilab.forge.scirenderer.shapes.geometry.Geometry;
34 import org.scilab.forge.scirenderer.shapes.geometry.Geometry.FaceCullingMode;
35 import org.scilab.forge.scirenderer.texture.AnchorPosition;
36 import org.scilab.forge.scirenderer.texture.Texture;
37 import org.scilab.forge.scirenderer.tranformations.Transformation;
38 import org.scilab.forge.scirenderer.tranformations.Vector3d;
39
40 /**
41  * @author Calixte DENIZET
42  */
43 public class Motor3D {
44
45     private Transformation transf;
46     private Transformation singleTransf;
47     private FaceCullingMode mode = FaceCullingMode.BOTH;
48     private Graphics2D g2d;
49     private Dimension dim;
50     private G2DTextureDrawingTools textureDrawingTools;
51     private G2DCanvas canvas;
52
53     /**
54      * Default constructor
55      * @param g2d a Graphics2D object where to draw
56      * @param dim the graphic dimensions
57      */
58     public Motor3D(G2DCanvas canvas, Graphics2D g2d, Dimension dim) {
59         this.canvas = canvas;
60         this.g2d = g2d;
61         this.dim = dim;
62         this.textureDrawingTools = new G2DTextureDrawingTools(g2d);
63         AbstractDrawable3DObject.resetDefaultPrecedence();
64     }
65
66     public void setGraphics(Graphics2D g2d) {
67         this.g2d = g2d;
68         this.textureDrawingTools.setGraphics(g2d);
69     }
70
71     public void setAntialiased(boolean aa) {
72         if (aa) {
73             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
74         } else {
75             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
76         }
77     }
78
79     public boolean is2DView() {
80         return canvas.getMainDrawer().is2DView();
81     }
82
83     public void setClippingPlanes(List<ClippingPlane> clippingPlanes) {
84         Scene.setClippingPlanes(clippingPlanes);
85     }
86
87     /**
88      * Set the face culling mode
89      * @param mode the mode to set
90      */
91     public void setFaceCullingMode(FaceCullingMode mode) {
92         this.mode = mode;
93     }
94
95     /**
96      * Set the current transformation
97      * @param transf the transformation to set
98      */
99     public void setTransformation(Transformation transf, Transformation single) {
100         this.transf = transf;
101         this.singleTransf = single;
102     }
103
104     public Transformation getCurrentTransformation() {
105         return transf;
106     }
107
108     public Transformation getCurrentSingleTransformation() {
109         return singleTransf;
110     }
111
112     /**
113      * Reset this motor
114      * @param color the filling color
115      */
116     public void reset(Color color) {
117         transf = null;
118         mode = FaceCullingMode.BOTH;
119         g2d.setColor(color);
120         g2d.fillRect(0, 0, (int) dim.getWidth(), (int) dim.getHeight());
121         Scene.clear();
122     }
123
124     /**
125      * Clear the depth buffer
126      */
127     public void clearDepth() {
128         Scene.clearDepth();
129     }
130
131     /**
132      * Draw the scene in the Graphics2D
133      */
134     public void draw() {
135         Scene.drawRoot(g2d);
136         Scene.clearAll();
137         G2DTextureManager.clear();
138     }
139
140     public void drawTexture(DrawingTools drawingTools, BufferedImage image, Texture texture) {
141         try {
142             SpritedRectangle o = new SpritedRectangle(new Vector3d(0, 0, 0), transf, image, texture.getMagnificationFilter());
143             add(o);
144         } catch (InvalidPolygonException e) { }
145     }
146
147     /**
148      * Add the geometry to the scene
149      * @param drawingTools the DrawingTools
150      * @param geometry the geometry to draw
151      * @param appearance the appearance to use
152      */
153     public void draw(DrawingTools drawingTools, Geometry geometry, Appearance appearance) {
154         setFaceCullingMode(geometry.getFaceCullingMode());
155         FloatBuffer vertexBuffer = geometry.getVertices().getData();
156
157         IntBuffer indicesBuffer = null;;
158         if (geometry.getIndices() != null) {
159             indicesBuffer = geometry.getIndices().getData();
160         }
161
162         IntBuffer wireIndicesBuffer = null;
163         if (geometry.getWireIndices() != null) {
164             wireIndicesBuffer = geometry.getWireIndices().getData();
165         }
166
167         FloatBuffer colorBuffer = null;
168         if (geometry.getColors() != null) {
169             colorBuffer = geometry.getColors().getData();
170         }
171
172         FloatBuffer normalBuffer = null;
173         if (geometry.getNormals() != null) {
174             normalBuffer = geometry.getNormals().getData();
175         }
176
177         Texture texture = appearance.getTexture();
178         FloatBuffer textureCoordinatesBuffer = null;
179         BufferedImage image = null;
180         if (texture != null && geometry.getTextureCoordinates() != null) {
181             textureCoordinatesBuffer = geometry.getTextureCoordinates().getData();
182             image = ((G2DTextureManager.G2DTexture) texture).getImage();
183         }
184
185         if (geometry.getFillDrawingMode() != Geometry.FillDrawingMode.NONE) {
186             addTriangles(vertexBuffer, normalBuffer, colorBuffer, appearance.getFillColor(), indicesBuffer, textureCoordinatesBuffer, image, texture, geometry.getFillDrawingMode());
187         }
188
189         if (geometry.getLineDrawingMode() != Geometry.LineDrawingMode.NONE) {
190             if (appearance.getLineColor() == null) {
191                 addSegments(vertexBuffer, colorBuffer, null, wireIndicesBuffer, geometry.getLineDrawingMode(), appearance);
192             } else {
193                 addSegments(vertexBuffer, null, appearance.getLineColor(), wireIndicesBuffer, geometry.getLineDrawingMode(), appearance);
194             }
195         }
196     }
197
198     public void draw(DrawingTools drawingTools, Texture texture, AnchorPosition anchor, ElementsBuffer positions, double rotationAngle) {
199         FloatBuffer positionsBuffer = positions.getData();
200         float[] buffer;
201
202         positionsBuffer.rewind();
203         if (positionsBuffer.hasArray()) {
204             buffer = positionsBuffer.array();
205         } else {
206             buffer = new float[positionsBuffer.limit()];
207             positionsBuffer.get(buffer);
208         }
209         Vector3d[] verticesArray = getMultiVectors(buffer, transf, false);
210
211         for (Vector3d v : verticesArray) {
212             try {
213                 SpritedRectangle o = new SpritedRectangle(v, texture, anchor, textureDrawingTools, rotationAngle);
214                 add(o);
215             } catch (InvalidPolygonException e) { }
216         }
217     }
218
219     public void draw(DrawingTools drawingTools, Texture texture, AnchorPosition anchor, Vector3d position, double rotationAngle) {
220         try {
221             add(new SpritedRectangle(transf.project(position), texture, anchor, textureDrawingTools, rotationAngle));
222         } catch (InvalidPolygonException e) { }
223     }
224
225     /**
226      * Add a Triangle to the scene
227      * @param tri the triangle to add
228      */
229     private void add(Triangle tri) {
230         Vector3d normal = tri.getNormal();
231         if (normal != null) {
232             //normal = transf.projectDirection(normal);
233             if ((mode == FaceCullingMode.CW && normal.getZ() > 0) || (mode == FaceCullingMode.CCW && normal.getZ() < 0) || mode == FaceCullingMode.BOTH) {
234                 Scene.addToRoot(is2DView(), tri);
235             }
236         } else {
237             Scene.addToRoot(is2DView(), tri);
238         }
239     }
240
241     /**
242      * Add a segment to the scene
243      * @param s the segment to add
244      */
245     private void add(Segment s) {
246         Scene.addToRoot(is2DView(), s);
247     }
248
249     private void add(SpritedRectangle sprite) {
250         Scene.addToRoot(is2DView(), sprite);
251     }
252
253     /**
254      * Get arrays from Buffer
255      * @param vertices a buffer containing vertices
256      * @param colors a buffer containing the colors
257      * @param defaultColor the color to use when colors is null
258      * @param indices a buffer containg the index of the vertices to retrieve
259      * @return an array of length 2 containing the vertices array and the colors array
260      */
261     private Object[] getArrays(FloatBuffer vertices, FloatBuffer colors, Color defaultColor, FloatBuffer textureCoords, IntBuffer indices) {
262         float[] buffer;
263         Vector3d[] verticesArray;
264         Vector3d[] textureCoordsArray = null;
265         Color[] colorsArray;
266
267         vertices.rewind();
268         if (vertices.hasArray()) {
269             buffer = vertices.array();
270         } else {
271             buffer = new float[vertices.limit()];
272             vertices.get(buffer);
273         }
274         verticesArray = getMultiVectors(buffer, transf, false);
275
276         if (colors != null) {
277             colors.rewind();
278             if (colors.hasArray()) {
279                 buffer = colors.array();
280             } else {
281                 buffer = new float[colors.limit()];
282                 colors.get(buffer);
283             }
284             colorsArray = getMultiColors(buffer);
285         } else {
286             colorsArray = new Color[vertices.limit()];
287             Arrays.fill(colorsArray, defaultColor);
288         }
289
290         if (textureCoords != null) {
291             textureCoords.rewind();
292             if (textureCoords.hasArray()) {
293                 buffer = textureCoords.array();
294             } else {
295                 buffer = new float[textureCoords.limit()];
296                 textureCoords.get(buffer);
297             }
298             textureCoordsArray = getMultiVectors(buffer);
299         }
300
301         if (indices != null) {
302             indices.rewind();
303             int[] ind;
304             if (indices.hasArray()) {
305                 ind = indices.array();
306             } else {
307                 ind = new int[indices.limit()];
308                 indices.get(ind);
309             }
310             Vector3d[] va = new Vector3d[ind.length];
311             Color[] ca = new Color[ind.length];
312             Vector3d[] ta = null;
313             if (textureCoords != null) {
314                 ta = new Vector3d[ind.length];
315             }
316             for (int i = 0; i < ind.length; i++) {
317                 va[i] = verticesArray[ind[i]];
318                 ca[i] = colorsArray[ind[i]];
319                 if (ta != null) {
320                     ta[i] = textureCoordsArray[ind[i]];
321                 }
322             }
323             verticesArray = va;
324             colorsArray = ca;
325             textureCoordsArray = ta;
326         }
327
328         return new Object[] {verticesArray, colorsArray, textureCoordsArray};
329     }
330
331     /**
332      * Convert the buffer vertices into vertices array and put the segments in the scene
333      * @param vertices a buffer containing vertices
334      * @param colors a buffer containing the colors
335      * @param defaultColor the color to use when colors is null
336      * @param indices a buffer containg the index of the vertices to retrieve
337      * @param drawingMode the drawing mode
338      * @param stroke the Stroke to use to draw a segment
339      */
340     private void addSegments(FloatBuffer vertices, FloatBuffer colors, Color defaultColor, IntBuffer indices, Geometry.LineDrawingMode drawingMode, Appearance appearance) {
341         Object[] arrays = getArrays(vertices, colors, defaultColor, null, indices);
342         Vector3d[] verticesArray = (Vector3d[]) arrays[0];
343         Color[] colorsArray = (Color[]) arrays[1];
344         Vector3d[] v;
345         Color[] c;
346         double cumLength = 0;
347
348         if (verticesArray.length <= 1) {
349             return;
350         }
351
352         switch (drawingMode) {
353             case SEGMENTS_STRIP :
354                 for (int i = 0; i < verticesArray.length - 1; i++) {
355                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]};
356                     c = new Color[] {colorsArray[i], colorsArray[i + 1]};
357                     try {
358                         add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength)));
359                         cumLength += Segment.getLength(v);
360                     } catch (InvalidPolygonException e) {
361                         cumLength = 0;
362                     }
363                 }
364                 break;
365             case SEGMENTS_LOOP :
366                 for (int i = 0; i < verticesArray.length - 1; i++) {
367                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]};
368                     c = new Color[] {colorsArray[i], colorsArray[i + 1]};
369                     try {
370                         add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength)));
371                         cumLength += Segment.getLength(v);
372                     } catch (InvalidPolygonException e) {
373                         cumLength = 0;
374                     }
375                 }
376                 int n = verticesArray.length - 1;
377                 v = new Vector3d[] {verticesArray[n], verticesArray[0]};
378                 c = new Color[] {colorsArray[n], colorsArray[0]};
379                 try {
380                     add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength)));
381                 } catch (InvalidPolygonException e) { }
382                 break;
383             case SEGMENTS :
384             default :
385                 for (int i = 0; i < verticesArray.length - 1; i += 2) {
386                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]};
387                     c = new Color[] {colorsArray[i], colorsArray[i + 1]};
388                     try {
389                         add(new Segment(v, c, G2DStroke.getStroke(appearance, 0)));
390                     } catch (InvalidPolygonException e) { }
391                 }
392                 break;
393         }
394     }
395
396     /**
397      * Convert the buffer vertices into vertices array and put the triangles in the scene
398      * @param vertices a buffer containing vertices
399      * @param normals a buffer containing the normals (not used)
400      * @param colors a buffer containing the colors
401      * @param defaultColor the color to use when colors is null
402      * @param indices a buffer containg the index of the vertices to retrieve
403      * @param drawingMode the drawing mode
404      */
405     private void addTriangles(FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors, Color defaultColor, IntBuffer indices, FloatBuffer textureCoords, final BufferedImage image, Texture texture, Geometry.FillDrawingMode drawingMode) {
406         Object[] arrays = getArrays(vertices, colors, defaultColor, textureCoords, indices);
407         Vector3d[] verticesArray = (Vector3d[]) arrays[0];
408         Color[] colorsArray = (Color[]) arrays[1];
409         Vector3d[] textureCoordsArray = (Vector3d[]) arrays[2];
410         Vector3d[] v;
411         Color[] c;
412         Texture.Filter filter = Texture.Filter.NEAREST;
413
414         if (texture != null) {
415             filter = texture.getMagnificationFilter();
416         }
417
418         switch (drawingMode) {
419             case TRIANGLE_FAN :
420                 for (int i = 1; i < verticesArray.length - 1; i++) {
421                     v = new Vector3d[] {verticesArray[0], verticesArray[i], verticesArray[i + 1]};
422                     try {
423                         if (image == null) {
424                             c = new Color[] {colorsArray[0], colorsArray[i], colorsArray[i + 1]};
425                             add(new Triangle(v, c, null));
426                         } else {
427                             add(new Triangle(v, new Vector3d[] {textureCoordsArray[0], textureCoordsArray[i], textureCoordsArray[i + 1]}, image, filter));
428                         }
429                     } catch (InvalidPolygonException e) { }
430                 }
431                 int n = verticesArray.length - 1;
432                 v = new Vector3d[] {verticesArray[0], verticesArray[n], verticesArray[1]};
433                 try {
434                     if (image == null) {
435                         c = new Color[] {colorsArray[0], colorsArray[n], colorsArray[1]};
436                         add(new Triangle(v, c, null));
437                     } else {
438                         add(new Triangle(v, new Vector3d[] {textureCoordsArray[0], textureCoordsArray[n], textureCoordsArray[1]}, image, filter));
439                     }
440                 } catch (InvalidPolygonException e) { }
441                 break;
442             case TRIANGLE_STRIP :
443                 for (int i = 0; i < verticesArray.length - 2; i++) {
444                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1], verticesArray[i + 2]};
445                     try {
446                         if (image == null) {
447                             c = new Color[] {colorsArray[i], colorsArray[i + 1], colorsArray[i + 2]};
448                             add(new Triangle(v, c, null));
449                         } else {
450                             add(new Triangle(v, new Vector3d[] {textureCoordsArray[i], textureCoordsArray[i + 1], textureCoordsArray[i + 2]}, image, filter));
451                         }
452                     } catch (InvalidPolygonException e) { }
453                 }
454                 break;
455             case TRIANGLES :
456             default :
457                 for (int i = 0; i < verticesArray.length - 2; i += 3) {
458                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1], verticesArray[i + 2]};
459                     try {
460                         if (image == null) {
461                             c = new Color[] {colorsArray[i], colorsArray[i + 1], colorsArray[i + 2]};
462                             add(new Triangle(v, c, null));
463                         } else {
464                             add(new Triangle(v, new Vector3d[] {textureCoordsArray[i], textureCoordsArray[i + 1], textureCoordsArray[i + 2]}, image, filter));
465                         }
466                     } catch (InvalidPolygonException e) { }
467                 }
468                 break;
469         }
470     }
471
472     /**
473      * Convert an array of float into an array of Vector3d objects
474      * @param vertices an array of float containing (vertices.length / G2DElementsBuffer.ELEMENT_SIZE) vectors coordinates
475      * @param t the transformation to use for the projection
476      * @param dir if true t.projectDirection() is used rather than t.project()
477      * @return an array of Vector3d containg the vertices
478      */
479     private static final Vector3d[] getMultiVectors(final float[] vertices, final Transformation t, final boolean dir) {
480         Vector3d[] v = new Vector3d[vertices.length / G2DElementsBuffer.ELEMENT_SIZE];
481         if (dir) {
482             int j = 0;
483             for (int i = 0; i < v.length; i++) {
484                 v[i] = t.projectDirection(new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2]));
485                 j += G2DElementsBuffer.ELEMENT_SIZE;
486             }
487         } else {
488             int j = 0;
489             for (int i = 0; i < v.length; i++) {
490                 v[i] = t.project(new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2]));
491                 j += G2DElementsBuffer.ELEMENT_SIZE;
492             }
493         }
494
495         return v;
496     }
497
498     /**
499      * Convert an array of float into an array of Vector3d objects
500      * @param vertices an array of float containing (vertices.length / G2DElementsBuffer.ELEMENT_SIZE) vectors coordinates
501      * @return an array of Vector3d containg the vertices
502      */
503     private static final Vector3d[] getMultiVectors(final float[] vertices) {
504         Vector3d[] v = new Vector3d[vertices.length / G2DElementsBuffer.ELEMENT_SIZE];
505         int j = 0;
506         for (int i = 0; i < v.length; i++) {
507             v[i] = new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2]);
508             j += G2DElementsBuffer.ELEMENT_SIZE;
509         }
510
511         return v;
512     }
513
514     /**
515      * Convert a float array into a Color array
516      * @param colors a float array
517      * @return an array of Color
518      */
519     private static final Color[] getMultiColors(final float[] colors) {
520         Color[] c = new Color[colors.length / G2DElementsBuffer.ELEMENT_SIZE];
521         int j = 0;
522         Color prev = Color.BLACK;
523         for (int i = 0; i < c.length; i++) {
524             c[i] = new Color(colors[j], colors[j + 1], colors[j + 2], colors[j + 3]);
525             if (prev.equals(c[i])) {
526                 c[i] = prev;
527             }
528             prev = c[i];
529             j += G2DElementsBuffer.ELEMENT_SIZE;
530         }
531
532         return c;
533     }
534 }