1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2013 - Pedro SOUZA
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.nio.FloatBuffer;
19 import java.nio.IntBuffer;
20 
21 
22 import org.scilab.forge.scirenderer.tranformations.Vector3f;
23 import org.scilab.forge.scirenderer.implementation.g2d.lighting.G2DLight;
24 import org.scilab.forge.scirenderer.shapes.appearance.Material;
25 
26 /**
27  * @author Pedro SOUZA
28  */
29 public class LightHelper {
30 
31     /**
32      * @param buffer the float buffer.
33      * @param stride the stride between elements.
34      * @return an array of Vector3f from the given float buffer.
35      */
getVector3f(FloatBuffer buffer, int stride)36     public static Vector3f[] getVector3f(FloatBuffer buffer, int stride) {
37         if (buffer == null) {
38             return null;
39         }
40         if (stride < 3) {
41             return null;
42         }
43 
44         float[] floats;
45         buffer.rewind();
46         if (buffer.hasArray()) {
47             floats = buffer.array();
48         } else {
49             floats = new float[buffer.limit()];
50             buffer.get(floats);
51         }
52 
53         Vector3f[] ret = new Vector3f[floats.length / stride];
54         for (int i = 0; i < floats.length; i += stride) {
55             ret[i] = new Vector3f(floats[i], floats[i + 1], floats[i + 2]);
56         }
57         return ret;
58     }
59 
60     /**
61      * @param buffer the float buffer.
62      * @param index the indices  buffer.
63      * @param stride the stride between elements.
64      * @param transf matrix to transform the vector, if null no transformation is applied.
65      * @return an array of Vector3f from the given float buffer.
66      */
getIndexedVector3f(FloatBuffer buffer, IntBuffer index, int stride, float[] transf)67     public static Vector3f[] getIndexedVector3f(FloatBuffer buffer, IntBuffer index, int stride, float[] transf) {
68         if (buffer == null || index == null) {
69             return null;
70         }
71         if (stride < 3) {
72             return null;
73         }
74 
75         float[] floats;
76         buffer.rewind();
77         if (buffer.hasArray()) {
78             floats = buffer.array();
79         } else {
80             floats = new float[buffer.limit()];
81             buffer.get(floats);
82         }
83 
84         int[] idx;
85         index.rewind();
86         if (index.hasArray()) {
87             idx = index.array();
88         } else {
89             idx = new int[index.limit()];
90             index.get(idx);
91         }
92 
93         Vector3f[] ret = new Vector3f[idx.length];
94         float x, y, z;
95         if (transf != null && transf.length == 16) {
96             for (int i = 0; i < idx.length; ++i) {
97                 ret[i] = transform(floats[stride * idx[i]], floats[stride * idx[i] + 1], floats[stride * idx[i] + 2], transf);
98             }
99         } else {
100             for (int i = 0; i < idx.length; ++i) {
101                 ret[i] = new Vector3f(floats[stride * idx[i]], floats[stride * idx[i] + 1], floats[stride * idx[i] + 2]);
102             }
103         }
104         return ret;
105     }
106 
transform(float x, float y, float z, float[] transf)107     static Vector3f transform(float x, float y, float z, float[] transf) {
108         float xx = transf[0] * x + transf[4] * y + transf[8] * z + transf[12];
109         float yy = transf[1] * x + transf[5] * y + transf[9] * z + transf[13];
110         float zz = transf[2] * x + transf[6] * y + transf[10] * z + transf[14];
111         return new Vector3f(xx, yy, zz);
112     }
113 
transformDirection(float x, float y, float z, float[] transf)114     static Vector3f transformDirection(float x, float y, float z, float[] transf) {
115         float xx = transf[0] * x + transf[4] * y + transf[8] * z;
116         float yy = transf[1] * x + transf[5] * y + transf[9] * z;
117         float zz = transf[2] * x + transf[6] * y + transf[10] * z;
118         return new Vector3f(xx, yy, zz);
119     }
120 
121     /**
122      * Apply the given ambient color to the output.
123      * @param ambient the ambient color.
124      * @param output the color vector to apply the ambient color.
125      * @param additive if true the ambient color is added to output.
126      * @return the resulting color vector.
127      */
applyAmbient(Color ambient, Color[] output, boolean additive)128     public static Color[] applyAmbient(Color ambient, Color[] output, boolean additive) {
129         for (int i = 0; i < output.length; ++i) {
130             if (additive) {
131                 output[i] = getColorSum(ambient, output[i]);
132             } else {
133                 output[i] = ambient;
134             }
135         }
136         return output;
137     }
138 
139     /**
140      * Apply the given ambient color to the output.
141      * @param ambient the ambient color.
142      * @param input the input color.
143      * @param output the color vector to apply the ambient color.
144      * @param additive if true the ambient color is added to output.
145      * @return the resulting color vector.
146      */
applyAmbient(Color ambient, Color[] input, Color[] output, boolean additive)147     public static Color[] applyAmbient(Color ambient, Color[] input, Color[] output, boolean additive) {
148         for (int i = 0; i < output.length; ++i) {
149             if (additive) {
150                 output[i] = getColorSum(getColorProduct(ambient, input[i]), output[i]);
151             } else {
152                 output[i] = getColorProduct(ambient, input[i]);
153             }
154         }
155         return output;
156     }
157 
158     /**
159      * Apply diffuse light to the output colors
160      * @param light the light position or direction.
161      * @param directional if true the vector light is considered a direction otherwise a position.
162      * @param vertices the surface vertices.
163      * @param normals the surface normals.
164      * @param colors the surface per-vertex colors.
165      * @param dffuse the light diffuse color.
166      * @param output the output color vector.
167      * @param additive if true the calculated diffuse color is added to the output.
168      * @return the resulting color vector.
169      */
applyDiffuse(Vector3f light, boolean directional, Vector3f[] vertices, Vector3f[] normals, Color[] colors, Color diffuse, Color[] output, boolean additive)170     public static Color[] applyDiffuse(Vector3f light, boolean directional, Vector3f[] vertices, Vector3f[] normals, Color[] colors, Color diffuse, Color[] output, boolean additive) {
171         float ndotl;
172         for (int i = 0; i < colors.length; ++i) {
173 
174             if (directional) {
175                 ndotl = normals[i].scalar(light);
176             } else {
177                 Vector3f ray = light.minus(vertices[i]).getNormalized();
178                 ndotl = normals[i].scalar(ray);
179             }
180             ndotl = clamp(ndotl);
181             Color c = getColorProduct(colors[i], diffuse);
182             if (additive) {
183                 output[i] = getColorSum(getColorProduct(c, ndotl), output[i]);
184             } else {
185                 output[i] = getColorProduct(c, ndotl);
186             }
187         }
188         return output;
189     }
190 
191     /**
192      * Apply diffuse light to the output colors
193      * @param light the light position or direction.
194      * @param directional if true the vector light is considered a direction otherwise a position.
195      * @param vertices the surface vertices.
196      * @param normals the surface normals.
197      * @param color the surface color.
198      * @param output the output color vector.
199      * @param additive if true the calculated diffuse color is added to the output.
200      * @return the resulting color vector.
201      */
applyDiffuse(Vector3f light, boolean directional, Vector3f[] vertices, Vector3f[] normals, Color color, Color[] output, boolean additive)202     public static Color[] applyDiffuse(Vector3f light, boolean directional, Vector3f[] vertices, Vector3f[] normals, Color color, Color[] output, boolean additive) {
203         float ndotl;
204         for (int i = 0; i < output.length; ++i) {
205 
206             if (directional) {
207                 ndotl = normals[i].scalar(light);
208             } else {
209                 Vector3f ray = light.minus(vertices[i]).getNormalized();
210                 ndotl = normals[i].scalar(ray);
211             }
212             ndotl = clamp(ndotl);
213             if (additive) {
214                 output[i] = getColorSum(getColorProduct(color, ndotl), output[i]);
215             } else {
216                 output[i] = getColorProduct(color, ndotl);
217             }
218         }
219         return output;
220     }
221 
applySpecular(Vector3f camera, Vector3f light, float shininess, boolean directional, Vector3f[] vertices, Vector3f[] normals, Color specular, Color[] output, boolean additive)222     public static Color[] applySpecular(Vector3f camera, Vector3f light, float shininess, boolean directional, Vector3f[] vertices, Vector3f[] normals, Color specular, Color[] output, boolean additive) {
223 
224         for (int i = 0; i < output.length; ++i) {
225 
226             Vector3f view = camera.minus(vertices[i]).getNormalized();
227             Vector3f half;
228             float ndotl;
229             if (directional) {
230                 half = view.plus(light);
231                 ndotl = normals[i].scalar(light);
232             } else {
233                 Vector3f ray = light.minus(vertices[i]).getNormalized();
234                 half = view.plus(ray);
235                 ndotl = normals[i].scalar(ray);
236             }
237             half = half.getNormalized();
238 
239             float s = 0.0f;
240             if (ndotl > 0.0f) {
241                 s = normals[i].scalar(half);
242                 s = clamp(s);
243                 s = (float)Math.pow((double)s, (double)shininess);
244             }
245 
246             if (additive) {
247                 output[i] = getColorSum(getColorProduct(specular, s), output[i]);
248             } else {
249                 output[i] = getColorProduct(specular, s);
250             }
251         }
252         return output;
253     }
254 
255     /**
256      * Apply a per-vertex lighting to the given colors
257      * @param light the light.
258      * @param mat the material properties.
259      * @param camera the camera position.
260      * @param vertices the surface vertices.
261      * @param normals the surface normals.
262      * @param colors the surface per-vertex colors.
263      * @param output the output color vector.
264      * @param transf the light transformation matrix. If null no transformation is applyed.
265      * @param additive if true the calculated color is added to the output.
266      * @return the resulting color vector.
267      */
applyLight(G2DLight light, Material mat, Vector3f camera, Vector3f[] vertices, Vector3f[] normals, Color[] colors, Color[] output, float[] transf, boolean additive)268     public static Color[] applyLight(G2DLight light, Material mat, Vector3f camera, Vector3f[] vertices, Vector3f[] normals, Color[] colors, Color[] output, float[] transf, boolean additive) {
269         Color ambient = getColorProduct(mat.getAmbientColor(), light.getAmbientColor());
270         Color diffuse = getColorProduct(mat.getDiffuseColor(), light.getDiffuseColor());
271         Color specular = getColorProduct(mat.getSpecularColor(), light.getSpecularColor());
272 
273         Color[] finalColor;
274         if (mat.isColorMaterialEnable()) {
275             finalColor = applyAmbient(light.getAmbientColor(), colors, output, additive);
276         } else {
277             finalColor = applyAmbient(ambient, output, additive);
278         }
279 
280         float[] v;
281         if (light.isPoint()) {
282             v = light.getPosition().getDataAsFloatArray();
283         } else {
284             v = light.getDirection().getDataAsFloatArray();
285         }
286 
287         Vector3f vec;
288         if (transf != null && transf.length == 16) {
289             if (light.isPoint()) {
290                 vec = transform(v[0], v[1], v[2], transf);
291             } else {
292                 vec = transformDirection(v[0], v[1], v[2], transf).getNormalized();
293             }
294         } else {
295             vec = new Vector3f(v[0], v[1], v[2]);
296         }
297 
298         if (mat.isColorMaterialEnable()) {
299             finalColor = applyDiffuse(vec, !light.isPoint(), vertices, normals, colors, light.getDiffuseColor(), finalColor, true);
300         } else {
301             finalColor = applyDiffuse(vec, !light.isPoint(), vertices, normals, diffuse, finalColor, true);
302         }
303 
304         finalColor = applySpecular(camera, vec, mat.getShininess(), !light.isPoint(), vertices, normals, specular, finalColor, true);
305 
306         return finalColor;
307     }
308 
309     /**
310      * return the product of the given colors
311      */
getColorProduct(Color a, Color b)312     private static Color getColorProduct(Color a, Color b) {
313         float[] ca = a.getComponents(null);
314         float[] cb = b.getComponents(null);
315         return new Color(ca[0] * cb[0], ca[1] * cb[1], ca[2] * cb[2]);
316     }
317 
318     /**
319      * return the clamped product of the color
320      */
getColorProduct(Color a, float f)321     private static Color getColorProduct(Color a, float f) {
322         float[] ca = a.getComponents(null);
323         return new Color(clamp(ca[0] * f), clamp(ca[1] * f), clamp(ca[2] * f));
324     }
325 
326     /**
327      * return the clamped sum of the given colors
328      */
getColorSum(Color a, Color b)329     private static Color getColorSum(Color a, Color b) {
330         float[] ca = a.getComponents(null);
331         float[] cb = b.getComponents(null);
332         return new Color(clamp(ca[0] + cb[0]), clamp(ca[1] + cb[1]), clamp(ca[2] + cb[2]));
333     }
334 
335     /**
336      * Clamp the given value to [0, 1]
337      */
clamp(float f)338     private static float clamp(float f) {
339         f = f < 0.0f ? 0.0f : f;
340         f = f > 1.0f ? 1.0f : f;
341         return f;
342     }
343 
reflect(Vector3f I, Vector3f N)344     static Vector3f reflect(Vector3f I, Vector3f N) {
345         return I.minus(N.times(2 * I.scalar(N)));
346     }
347 }
348