1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2012-2013 - Scilab Enterprises - Calixte Denizet
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  */
14 
15 package org.scilab.forge.scirenderer.implementation.g2d.motor;
16 
17 import java.awt.Color;
18 import java.awt.Dimension;
19 import java.awt.Graphics2D;
20 import java.awt.RenderingHints;
21 import java.awt.Stroke;
22 import java.awt.image.BufferedImage;
23 import java.nio.FloatBuffer;
24 import java.nio.IntBuffer;
25 import java.util.Arrays;
26 import java.util.List;
27 
28 import org.scilab.forge.scirenderer.DrawingTools;
29 import org.scilab.forge.scirenderer.buffers.ElementsBuffer;
30 import org.scilab.forge.scirenderer.clipping.ClippingPlane;
31 import org.scilab.forge.scirenderer.implementation.g2d.G2DCanvas;
32 import org.scilab.forge.scirenderer.implementation.g2d.buffers.G2DElementsBuffer;
33 import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureDrawingTools;
34 import org.scilab.forge.scirenderer.implementation.g2d.texture.G2DTextureManager;
35 import org.scilab.forge.scirenderer.shapes.appearance.Appearance;
36 import org.scilab.forge.scirenderer.shapes.geometry.Geometry;
37 import org.scilab.forge.scirenderer.shapes.geometry.Geometry.FaceCullingMode;
38 import org.scilab.forge.scirenderer.texture.AnchorPosition;
39 import org.scilab.forge.scirenderer.texture.Texture;
40 import org.scilab.forge.scirenderer.tranformations.Transformation;
41 import org.scilab.forge.scirenderer.tranformations.Vector3d;
42 import org.scilab.forge.scirenderer.tranformations.Vector3f;
43 
44 import org.scilab.forge.scirenderer.lightning.Light;
45 import org.scilab.forge.scirenderer.lightning.LightManager;
46 import org.scilab.forge.scirenderer.implementation.g2d.lighting.G2DLight;
47 import org.scilab.forge.scirenderer.implementation.g2d.lighting.G2DLightManager;
48 import org.scilab.forge.scirenderer.shapes.appearance.Material;
49 
50 /**
51  * @author Calixte DENIZET
52  */
53 public class Motor3D {
54 
55     private Transformation transf;
56     private Transformation singleTransf;
57     private FaceCullingMode mode = FaceCullingMode.BOTH;
58     private Graphics2D g2d;
59     private Dimension dim;
60     private G2DTextureDrawingTools textureDrawingTools;
61     private G2DCanvas canvas;
62 
63     /**
64      * Default constructor
65      * @param g2d a Graphics2D object where to draw
66      * @param dim the graphic dimensions
67      */
Motor3D(G2DCanvas canvas, Graphics2D g2d, Dimension dim)68     public Motor3D(G2DCanvas canvas, Graphics2D g2d, Dimension dim) {
69         this.canvas = canvas;
70         this.g2d = g2d;
71         this.dim = dim;
72         this.textureDrawingTools = new G2DTextureDrawingTools(g2d);
73         AbstractDrawable3DObject.resetDefaultPrecedence();
74     }
75 
setGraphics(Graphics2D g2d)76     public void setGraphics(Graphics2D g2d) {
77         this.g2d = g2d;
78         this.textureDrawingTools.setGraphics(g2d);
79     }
80 
setAntialiased(boolean aa)81     public void setAntialiased(boolean aa) {
82         if (aa) {
83             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
84         } else {
85             g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
86         }
87     }
88 
is2DView()89     public boolean is2DView() {
90         return canvas.getMainDrawer().is2DView();
91     }
92 
setClippingPlanes(List<ClippingPlane> clippingPlanes)93     public void setClippingPlanes(List<ClippingPlane> clippingPlanes) {
94         Scene.setClippingPlanes(clippingPlanes);
95     }
96 
97     /**
98      * Set the face culling mode
99      * @param mode the mode to set
100      */
setFaceCullingMode(FaceCullingMode mode)101     public void setFaceCullingMode(FaceCullingMode mode) {
102         this.mode = mode;
103     }
104 
105     /**
106      * Set the current transformation
107      * @param transf the transformation to set
108      */
setTransformation(Transformation transf, Transformation single)109     public void setTransformation(Transformation transf, Transformation single) {
110         this.transf = transf;
111         this.singleTransf = single;
112     }
113 
getCurrentTransformation()114     public Transformation getCurrentTransformation() {
115         return transf;
116     }
117 
getCurrentSingleTransformation()118     public Transformation getCurrentSingleTransformation() {
119         return singleTransf;
120     }
121 
122     /**
123      * Reset this motor
124      * @param color the filling color
125      */
reset(Color color)126     public void reset(Color color) {
127         transf = null;
128         mode = FaceCullingMode.BOTH;
129         g2d.setColor(color);
130         g2d.fillRect(0, 0, (int) dim.getWidth(), (int) dim.getHeight());
131         Scene.clear();
132     }
133 
134     /**
135      * Clear the depth buffer
136      */
clearDepth()137     public void clearDepth() {
138         Scene.clearDepth();
139     }
140 
141     /**
142      * Draw the scene in the Graphics2D
143      */
draw()144     public void draw() {
145         Scene.drawRoot(g2d);
146         clean();
147     }
148 
clean()149     public void clean() {
150         Scene.clearAll();
151         G2DTextureManager.clear();
152     }
153 
drawTexture(DrawingTools drawingTools, BufferedImage image, Texture texture)154     public void drawTexture(DrawingTools drawingTools, BufferedImage image, Texture texture) {
155         try {
156             SpritedRectangle o = new SpritedRectangle(new Vector3d(0, 0, 0), transf, image, texture.getMagnificationFilter());
157             add(o);
158         } catch (InvalidPolygonException e) { }
159     }
160 
161     /**
162      * Add the geometry to the scene
163      * @param drawingTools the DrawingTools
164      * @param geometry the geometry to draw
165      * @param appearance the appearance to use
166      */
draw(DrawingTools drawingTools, Geometry geometry, Appearance appearance)167     public void draw(DrawingTools drawingTools, Geometry geometry, Appearance appearance) {
168         setFaceCullingMode(geometry.getFaceCullingMode());
169         FloatBuffer vertexBuffer = geometry.getVertices().getData();
170 
171         IntBuffer indicesBuffer = null;;
172         if (geometry.getIndices() != null) {
173             indicesBuffer = geometry.getIndices().getData();
174         }
175 
176         IntBuffer wireIndicesBuffer = null;
177         if (geometry.getWireIndices() != null) {
178             wireIndicesBuffer = geometry.getWireIndices().getData();
179         }
180 
181         FloatBuffer colorBuffer = null;
182         if (geometry.getColors() != null) {
183             colorBuffer = geometry.getColors().getData();
184         }
185 
186         FloatBuffer normalBuffer = null;
187         if (geometry.getNormals() != null) {
188             normalBuffer = geometry.getNormals().getData();
189         }
190 
191         Texture texture = appearance.getTexture();
192         FloatBuffer textureCoordinatesBuffer = null;
193         BufferedImage image = null;
194         if (texture != null && geometry.getTextureCoordinates() != null) {
195             textureCoordinatesBuffer = geometry.getTextureCoordinates().getData();
196             image = ((G2DTextureManager.G2DTexture) texture).getImage();
197         }
198 
199         G2DLightManager lm = (G2DLightManager)drawingTools.getLightManager();
200         lm.setMaterial(appearance.getMaterial());
201 
202         if (geometry.getFillDrawingMode() != Geometry.FillDrawingMode.NONE) {
203             addTriangles(vertexBuffer, normalBuffer, colorBuffer, appearance.getFillColor(), indicesBuffer, textureCoordinatesBuffer, image, texture, geometry.getFillDrawingMode(), lm);
204         }
205 
206         if (geometry.getLineDrawingMode() != Geometry.LineDrawingMode.NONE) {
207             if (appearance.getLineColor() == null) {
208                 addSegments(vertexBuffer, colorBuffer, null, wireIndicesBuffer, geometry.getLineDrawingMode(), appearance);
209             } else {
210                 addSegments(vertexBuffer, null, appearance.getLineColor(), wireIndicesBuffer, geometry.getLineDrawingMode(), appearance);
211             }
212         }
213     }
214 
draw(DrawingTools drawingTools, Texture texture, AnchorPosition anchor, ElementsBuffer positions, int offset, int stride, double rotationAngle, org.scilab.forge.scirenderer.shapes.appearance.Color auxColor, ElementsBuffer colors)215     public void draw(DrawingTools drawingTools, Texture texture, AnchorPosition anchor, ElementsBuffer positions, int offset, int stride, double rotationAngle, org.scilab.forge.scirenderer.shapes.appearance.Color auxColor, ElementsBuffer colors) {
216         FloatBuffer positionsBuffer = positions.getData();
217         float[] buffer;
218         offset = offset < 0 ? 0 : offset;
219         stride = stride < 1 ? 1 : stride;
220         Color[] colorsArray = null;
221 
222         positionsBuffer.rewind();
223         if (positionsBuffer.hasArray()) {
224             buffer = positionsBuffer.array();
225         } else {
226             buffer = new float[positionsBuffer.limit()];
227             positionsBuffer.get(buffer);
228         }
229         Vector3d[] verticesArray = getMultiVectors(buffer, transf, false);
230 
231         if (colors != null) {
232             FloatBuffer colorsBuffer = colors.getData();
233             colorsBuffer.rewind();
234             if (colorsBuffer.hasArray()) {
235                 buffer = colorsBuffer.array();
236             } else {
237                 buffer = new float[colorsBuffer.limit()];
238                 colorsBuffer.get(buffer);
239             }
240             colorsArray = getMultiColors(buffer);
241         }
242 
243         for (int i = offset; i < verticesArray.length; i += stride) {
244             try {
245                 Vector3d v = verticesArray[i];
246                 SpritedRectangle o;
247                 if (colorsArray == null) {
248                     o = new SpritedRectangle(v, texture, anchor, textureDrawingTools, rotationAngle, null, null);
249                 } else {
250                     o = new SpritedRectangle(v, texture, anchor, textureDrawingTools, rotationAngle, (Color) auxColor, colorsArray[i]);
251                 }
252                 add(o);
253             } catch (InvalidPolygonException e) { }
254         }
255     }
256 
257     public void draw(DrawingTools drawingTools, Texture texture, AnchorPosition anchor, Vector3d position, double rotationAngle) {
258         try {
259             add(new SpritedRectangle(transf.project(position), texture, anchor, textureDrawingTools, rotationAngle, null, null));
260         } catch (InvalidPolygonException e) { }
261     }
262 
263     /**
264      * Add a Triangle to the scene
265      * @param tri the triangle to add
266      */
267     private void add(Triangle tri) {
268         final boolean is2d = is2DView();
269         if (is2d) {
270             Scene.addToRoot(is2d, tri);
271         } else {
272             Vector3d normal = tri.getNormal();
273             if (normal != null) {
274                 //normal = transf.projectDirection(normal);
275                 if ((mode == FaceCullingMode.CW && normal.getZ() > 0) || (mode == FaceCullingMode.CCW && normal.getZ() < 0) || mode == FaceCullingMode.BOTH) {
276                     Scene.addToRoot(is2d, tri);
277                 }
278             } else {
279                 Scene.addToRoot(is2d, tri);
280             }
281         }
282     }
283 
284     /**
285      * Add a segment to the scene
286      * @param s the segment to add
287      */
288     private void add(Segment s) {
289         Scene.addToRoot(is2DView(), s);
290     }
291 
292     private void add(SpritedRectangle sprite) {
293         Scene.addToRoot(is2DView(), sprite);
294     }
295 
296     private void add(PolyLine p) {
297         Scene.addToRoot(is2DView(), p);
298     }
299 
300     /**
301      * Get arrays from Buffer
302      * @param vertices a buffer containing vertices
303      * @param colors a buffer containing the colors
304      * @param defaultColor the color to use when colors is null
305      * @param indices a buffer containing the index of the vertices to retrieve
306      * @return an array of length 2 containing the vertices array and the colors array
307      */
308     private Object[] getArrays(FloatBuffer vertices, FloatBuffer colors, Color defaultColor, FloatBuffer textureCoords, IntBuffer indices) {
309         float[] buffer;
310         Vector3d[] verticesArray;
311         Vector3d[] textureCoordsArray = null;
312         Color[] colorsArray;
313 
314         vertices.rewind();
315         if (vertices.hasArray()) {
316             buffer = vertices.array();
317         } else {
318             buffer = new float[vertices.limit()];
319             vertices.get(buffer);
320         }
321         verticesArray = getMultiVectors(buffer, transf, false);
322 
323         if (colors != null) {
324             colors.rewind();
325             if (colors.hasArray()) {
326                 buffer = colors.array();
327             } else {
328                 buffer = new float[colors.limit()];
329                 colors.get(buffer);
330             }
331             colorsArray = getMultiColors(buffer);
332         } else {
333             colorsArray = new Color[vertices.limit() / G2DElementsBuffer.ELEMENT_SIZE];
334             Arrays.fill(colorsArray, defaultColor);
335         }
336 
337         if (textureCoords != null) {
338             textureCoords.rewind();
339             if (textureCoords.hasArray()) {
340                 buffer = textureCoords.array();
341             } else {
342                 buffer = new float[textureCoords.limit()];
343                 textureCoords.get(buffer);
344             }
345             textureCoordsArray = getMultiVectors(buffer);
346         }
347 
348         if (indices != null) {
349             indices.rewind();
350             int[] ind;
351             if (indices.hasArray()) {
352                 ind = indices.array();
353             } else {
354                 ind = new int[indices.limit()];
355                 indices.get(ind);
356             }
357             Vector3d[] va = new Vector3d[ind.length];
358             Color[] ca = new Color[ind.length];
359             Vector3d[] ta = null;
360             if (textureCoords != null) {
361                 ta = new Vector3d[ind.length];
362             }
363 
364             for (int i = 0; i < ind.length; i++) {
365                 va[i] = verticesArray[ind[i]];
366                 ca[i] = colorsArray[ind[i]];
367                 if (ta != null) {
368                     ta[i] = textureCoordsArray[ind[i]];
369                 }
370             }
371             verticesArray = va;
372             colorsArray = ca;
373             textureCoordsArray = ta;
374         }
375 
376         return new Object[] {verticesArray, colorsArray, textureCoordsArray};
377     }
378 
379     /**
380      * Convert the buffer vertices into vertices array and put the segments in the scene
381      * @param vertices a buffer containing vertices
382      * @param colors a buffer containing the colors
383      * @param defaultColor the color to use when colors is null
384      * @param indices a buffer containing the index of the vertices to retrieve
385      * @param drawingMode the drawing mode
386      * @param stroke the Stroke to use to draw a segment
387      */
388     private void addSegments(FloatBuffer vertices, FloatBuffer colors, Color defaultColor, IntBuffer indices, Geometry.LineDrawingMode drawingMode, Appearance appearance) {
389         Object[] arrays = getArrays(vertices, colors, defaultColor, null, indices);
390         Vector3d[] verticesArray = (Vector3d[]) arrays[0];
391         Color[] colorsArray = (Color[]) arrays[1];
392         Vector3d[] v;
393         Color[] c;
394         double cumLength = 0;
395 
396         if (verticesArray.length <= 1) {
397             return;
398         }
399 
400         switch (drawingMode) {
401             case SEGMENTS_STRIP :
402                 if (is2DView()) {
403                     List<PolyLine> list = PolyLine.getPolyLines(verticesArray, colorsArray, G2DStroke.getStroke(appearance, 0), false);
404                     for (PolyLine p : list) {
405                         add(p);
406                     }
407                 } else {
408                     for (int i = 0; i < verticesArray.length - 1; i++) {
409                         v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]};
410                         c = new Color[] {colorsArray[i], colorsArray[i + 1]};
411                         try {
412                             add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength), false));
413                             cumLength += Segment.getLength(v);
414                         } catch (InvalidPolygonException e) {
415                             cumLength = 0;
416                         }
417                     }
418                 }
419                 break;
420             case SEGMENTS_LOOP :
421                 if (is2DView()) {
422                     List<PolyLine> list = PolyLine.getPolyLines(verticesArray, colorsArray, G2DStroke.getStroke(appearance, 0), true);
423                     for (PolyLine p : list) {
424                         add(p);
425                     }
426                 } else {
427                     for (int i = 0; i < verticesArray.length - 1; i++) {
428                         v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]};
429                         c = new Color[] {colorsArray[i], colorsArray[i + 1]};
430                         try {
431                             add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength), false));
432                             cumLength += Segment.getLength(v);
433                         } catch (InvalidPolygonException e) {
434                             cumLength = 0;
435                         }
436                     }
437                     int n = verticesArray.length - 1;
438                     v = new Vector3d[] {verticesArray[n], verticesArray[0]};
439                     c = new Color[] {colorsArray[n], colorsArray[0]};
440                     try {
441                         add(new Segment(v, c, G2DStroke.getStroke(appearance, cumLength), false));
442                     } catch (InvalidPolygonException e) { }
443                 }
444                 break;
445             case SEGMENTS :
446             default :
447                 for (int i = 0; i < verticesArray.length - 1; i += 2) {
448                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1]};
449                     c = new Color[] {colorsArray[i], colorsArray[i + 1]};
450                     try {
451                         add(new Segment(v, c, G2DStroke.getStroke(appearance, 0), is2DView()));
452                     } catch (InvalidPolygonException e) { }
453                 }
454                 break;
455         }
456     }
457 
458     /**
459      * Convert the buffer vertices into vertices array and put the triangles in the scene
460      * @param vertices a buffer containing vertices
461      * @param normals a buffer containing the normals (not used)
462      * @param colors a buffer containing the colors
463      * @param defaultColor the color to use when colors is null
464      * @param indices a buffer containing the index of the vertices to retrieve
465      * @param drawingMode the drawing mode
466      */
467     private void addTriangles(FloatBuffer vertices, FloatBuffer normals, FloatBuffer colors, Color defaultColor, IntBuffer indices, FloatBuffer textureCoords, final BufferedImage image, Texture texture, Geometry.FillDrawingMode drawingMode, G2DLightManager lightManager) {
468         Object[] arrays = getArrays(vertices, colors, defaultColor, textureCoords, indices);
469         Vector3d[] verticesArray = (Vector3d[]) arrays[0];
470         Color[] colorsArray = (Color[]) arrays[1];
471         Vector3d[] textureCoordsArray = (Vector3d[]) arrays[2];
472         Vector3d[] v;
473         Color[] c;
474         Texture.Filter filter = Texture.Filter.NEAREST;
475 
476         if (texture != null) {
477             filter = texture.getMagnificationFilter();
478         }
479 
480         colorsArray = applyLighting(vertices, normals, indices, colorsArray, lightManager);
481 
482         switch (drawingMode) {
483             case TRIANGLE_FAN :
484                 for (int i = 1; i < verticesArray.length - 1; i++) {
485                     v = new Vector3d[] {verticesArray[0], verticesArray[i], verticesArray[i + 1]};
486                     try {
487                         if (image == null) {
488                             c = new Color[] {colorsArray[0], colorsArray[i], colorsArray[i + 1]};
489                             add(new Triangle(v, c, null));
490                         } else {
491                             add(new Triangle(v, new Vector3d[] {textureCoordsArray[0], textureCoordsArray[i], textureCoordsArray[i + 1]}, image, filter));
492                         }
493                     } catch (InvalidPolygonException e) { }
494                 }
495                 int n = verticesArray.length - 1;
496                 v = new Vector3d[] {verticesArray[0], verticesArray[n], verticesArray[1]};
497                 try {
498                     if (image == null) {
499                         c = new Color[] {colorsArray[0], colorsArray[n], colorsArray[1]};
500                         add(new Triangle(v, c, null));
501                     } else {
502                         add(new Triangle(v, new Vector3d[] {textureCoordsArray[0], textureCoordsArray[n], textureCoordsArray[1]}, image, filter));
503                     }
504                 } catch (InvalidPolygonException e) { }
505                 break;
506             case TRIANGLE_STRIP :
507                 for (int i = 0; i < verticesArray.length - 2; i++) {
508                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1], verticesArray[i + 2]};
509                     try {
510                         if (image == null) {
511                             c = new Color[] {colorsArray[i], colorsArray[i + 1], colorsArray[i + 2]};
512                             add(new Triangle(v, c, null));
513                         } else {
514                             add(new Triangle(v, new Vector3d[] {textureCoordsArray[i], textureCoordsArray[i + 1], textureCoordsArray[i + 2]}, image, filter));
515                         }
516                     } catch (InvalidPolygonException e) { }
517                 }
518                 break;
519             case TRIANGLES :
520             default :
521                 for (int i = 0; i < verticesArray.length - 2; i += 3) {
522                     v = new Vector3d[] {verticesArray[i], verticesArray[i + 1], verticesArray[i + 2]};
523                     try {
524                         if (image == null) {
525                             c = new Color[] {colorsArray[i], colorsArray[i + 1], colorsArray[i + 2]};
526                             add(new Triangle(v, c, null));
527                         } else {
528                             add(new Triangle(v, new Vector3d[] {textureCoordsArray[i], textureCoordsArray[i + 1], textureCoordsArray[i + 2]}, image, filter));
529                         }
530                     } catch (InvalidPolygonException e) { }
531                 }
532                 break;
533         }
534     }
535 
536     /**
537      * Convert an array of float into an array of Vector3d objects
538      * @param vertices an array of float containing (vertices.length / G2DElementsBuffer.ELEMENT_SIZE) vectors coordinates
539      * @param t the transformation to use for the projection
540      * @param dir if true t.projectDirection() is used rather than t.project()
541      * @return an array of Vector3d containing the vertices
542      */
543     private static final Vector3d[] getMultiVectors(final float[] vertices, final Transformation t, final boolean dir) {
544         Vector3d[] v = new Vector3d[vertices.length / G2DElementsBuffer.ELEMENT_SIZE];
545         if (dir) {
546             int j = 0;
547             for (int i = 0; i < v.length; i++) {
548                 v[i] = t.projectDirection(new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2]));
549                 j += G2DElementsBuffer.ELEMENT_SIZE;
550             }
551         } else {
552             int j = 0;
553             for (int i = 0; i < v.length; i++) {
554                 v[i] = t.project(new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2]));
555                 j += G2DElementsBuffer.ELEMENT_SIZE;
556             }
557         }
558 
559         return v;
560     }
561 
562     /**
563      * Convert an array of float into an array of Vector3d objects
564      * @param vertices an array of float containing (vertices.length / G2DElementsBuffer.ELEMENT_SIZE) vectors coordinates
565      * @return an array of Vector3d containing the vertices
566      */
567     private static final Vector3d[] getMultiVectors(final float[] vertices) {
568         Vector3d[] v = new Vector3d[vertices.length / G2DElementsBuffer.ELEMENT_SIZE];
569         int j = 0;
570         for (int i = 0; i < v.length; i++) {
571             v[i] = new Vector3d(vertices[j], vertices[j + 1], vertices[j + 2]);
572             j += G2DElementsBuffer.ELEMENT_SIZE;
573         }
574 
575         return v;
576     }
577 
578     /**
579      * Convert a float array into a Color array
580      * @param colors a float array
581      * @return an array of Color
582      */
583     private static final Color[] getMultiColors(final float[] colors) {
584         Color[] c = new Color[colors.length / G2DElementsBuffer.ELEMENT_SIZE];
585         int j = 0;
586         Color prev = Color.BLACK;
587         for (int i = 0; i < c.length; i++) {
588             c[i] = new Color(colors[j], colors[j + 1], colors[j + 2], colors[j + 3]);
589             if (prev.equals(c[i])) {
590                 c[i] = prev;
591             }
592             prev = c[i];
593             j += G2DElementsBuffer.ELEMENT_SIZE;
594         }
595 
596         return c;
597     }
598 
599     /**
600      * Perform per-vertex lighting
601      */
602     private Color[] applyLighting(FloatBuffer vertices, FloatBuffer normals, IntBuffer index, Color[] colors, G2DLightManager lightManager) {
603 
604         if (!lightManager.isLightningEnable() || vertices == null || normals == null
605                 || index == null || colors == null) {
606             return colors;
607         }
608 
609         Material mat = lightManager.getMaterial();
610         if (mat == null) {
611             return colors;
612         }
613 
614         float[] vertexTransf = lightManager.getVertexTransform();
615         float[] normalTransf = lightManager.getNormalTransform();
616         //for transformed vertices camera is at origin.
617         Vector3f camera = new Vector3f(0.f, 0.f , 0.f);
618         Vector3f[] vertexArray = LightHelper.getIndexedVector3f(vertices, index, G2DElementsBuffer.ELEMENT_SIZE, vertexTransf);
619         Vector3f[] normalArray = LightHelper.getIndexedVector3f(normals, index, G2DElementsBuffer.ELEMENT_SIZE, normalTransf);
620 
621         for (int i = 0; i < normalArray.length; ++i) {
622             normalArray[i] = normalArray[i].getNormalized();
623         }
624 
625 
626         Color[] outColors = new Color[colors.length];
627         boolean first = true;
628         for (int i = 0; i < lightManager.getLightNumber(); ++i) {
629             G2DLight l = (G2DLight)lightManager.getLight(i);
630 
631             if (l == null || !l.isEnable()) {
632                 continue;
633             }
634 
635             outColors = LightHelper.applyLight(l, mat, camera, vertexArray, normalArray, colors, outColors, vertexTransf, !first);
636             first = false;
637         }
638         return outColors;
639     }
640 }
641