1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 
34 /*!
35   \page coin_shaders Shaders in Coin
36 
37   Coin 2.5 added support for shaders. The main nodes used are SoShaderProgram,
38   SoVertexShader, SoFragmentShader, and SoGeometryShader. A typical scene graph
39   with shaders will look something like this:
40 
41   \code
42 
43   Separator {
44     ShaderProgram {
45       shaderObject [
46         VertexShader {
47           sourceProgram "myvertexshader.glsl"
48           parameter [
49             ShaderParameter1f { name "myvertexparam" value 1.0 }
50           ]
51         }
52         FragmentShader {
53           sourceProgram "myfragmentshader.glsl"
54           parameter [
55             ShaderParameter1f { name "myfragmentparam" value 2.0 }
56           ]
57         }
58       ]
59     }
60     Cube { }
61   }
62 
63   \endcode
64 
65   This will render the Cube with the vertex and fragment shaders
66   specified in myvertexshader.glsl and myfragmentshader.glsl. Coin
67   also supports ARB shaders and Cg shaders (if the Cg library is
68   installed). However, we recommend using GLSL since we will focus
69   mostly on support this shader language.
70 
71   Coin defines some named parameters that can be added by the
72   application programmer, and which will be automatically updated by
73   Coin while traversing the scene graph.
74 
75   \li coin_texunit[n]_model - Set to 0 when texturing is disabled, and
76   to SoTextureImageElement::Model if there's a current texture on the state
77   for unit \a n.
78 
79   \li coin_light_model - Set to 1 for PHONG, 0 for BASE_COLOR lighting.
80 
81   \li coin_two_sided_lighting - Set to 1 for two-sided, 0 for normal
82 
83   Example scene graph that renders per-fragment OpenGL Phong lighting
84   for one light source. The shaders assume the first light source is a
85   directional light. This is the case if you open the file in a standard
86   examiner viewer.
87 
88   The iv-file:
89   \code
90   Separator {
91     ShaderProgram {
92       shaderObject [
93         VertexShader {
94           sourceProgram "perpixel_vertex.glsl"
95         }
96         FragmentShader {
97           sourceProgram "perpixel_fragment.glsl"
98         }
99       ]
100     }
101     Complexity { value 1.0 }
102     Material { diffuseColor 1 0 0 specularColor 1 1 1 shininess 0.9 }
103     Sphere { }
104 
105     Translation { translation 3 0 0 }
106     Material { diffuseColor 0 1 0 specularColor 1 1 1 shininess 0.9 }
107     Cone { }
108 
109     Translation { translation 3 0 0 }
110     Material { diffuseColor 0.8 0.4 0.1 specularColor 1 1 1 shininess 0.9 }
111     Cylinder { }
112   }
113   \endcode
114 
115   The vertex shader (perpixel_vertex.glsl):
116   \code
117   varying vec3 ecPosition3;
118   varying vec3 fragmentNormal;
119 
120   void main(void)
121   {
122     vec4 ecPosition = gl_ModelViewMatrix * gl_Vertex;
123     ecPosition3 = ecPosition.xyz / ecPosition.w;
124     fragmentNormal = normalize(gl_NormalMatrix * gl_Normal);
125 
126     gl_Position = ftransform();
127     gl_FrontColor = gl_Color;
128   }
129   \endcode
130 
131   The fragment shader (perpixel_fragment.glsl):
132   \code
133   varying vec3 ecPosition3;
134   varying vec3 fragmentNormal;
135 
136   void DirectionalLight(in int i,
137                         in vec3 normal,
138                         inout vec4 ambient,
139                         inout vec4 diffuse,
140                         inout vec4 specular)
141   {
142     float nDotVP; // normal . light direction
143     float nDotHV; // normal . light half vector
144     float pf;     // power factor
145 
146     nDotVP = max(0.0, dot(normal, normalize(vec3(gl_LightSource[i].position))));
147     nDotHV = max(0.0, dot(normal, vec3(gl_LightSource[i].halfVector)));
148 
149     if (nDotVP == 0.0)
150       pf = 0.0;
151     else
152       pf = pow(nDotHV, gl_FrontMaterial.shininess);
153 
154     ambient += gl_LightSource[i].ambient;
155     diffuse += gl_LightSource[i].diffuse * nDotVP;
156     specular += gl_LightSource[i].specular * pf;
157   }
158 
159   void main(void)
160   {
161     vec3 eye = -normalize(ecPosition3);
162     vec4 ambient = vec4(0.0);
163     vec4 diffuse = vec4(0.0);
164     vec4 specular = vec4(0.0);
165     vec3 color;
166 
167     DirectionalLight(0, normalize(fragmentNormal), ambient, diffuse, specular);
168 
169     color =
170       gl_FrontLightModelProduct.sceneColor.rgb +
171       ambient.rgb * gl_FrontMaterial.ambient.rgb +
172       diffuse.rgb * gl_Color.rgb +
173       specular.rgb * gl_FrontMaterial.specular.rgb;
174 
175     gl_FragColor = vec4(color, gl_Color.a);
176   }
177   \endcode
178 */
179 
180 #include "shaders/SoShader.h"
181 
182 #include <cassert>
183 #include <cstdio>
184 #include <cstdlib>
185 
186 #include <Inventor/nodes/SoShaderProgram.h>
187 #include <Inventor/nodes/SoShaderObject.h>
188 #include <Inventor/nodes/SoFragmentShader.h>
189 #include <Inventor/nodes/SoVertexShader.h>
190 #include <Inventor/nodes/SoGeometryShader.h>
191 #include <Inventor/nodes/SoShaderParameter.h>
192 #include <Inventor/elements/SoGLShaderProgramElement.h>
193 #include <Inventor/C/tidbits.h>
194 #include <Inventor/errors/SoDebugError.h>
195 
196 #include "glue/cg.h"
197 #include "misc/SbHash.h"
198 #include "tidbitsp.h"
199 
200 // *************************************************************************
201 
202 #include <data/shaders/lights/SpotLight.h>
203 #include <data/shaders/lights/PointLight.h>
204 #include <data/shaders/lights/DirectionalLight.h>
205 #include <data/shaders/lights/DirSpotLight.h>
206 #include <data/shaders/vsm/VsmLookup.h>
207 
208 // *************************************************************************
209 
210 static const char * SO_SHADER_DIR = NULL;
211 static SbHash<const char *, char *> * shader_dict = NULL;
212 static SbHash<const char *, char *> * shader_builtin_dict = NULL;
213 
214 static void
soshader_cleanup(void)215 soshader_cleanup(void)
216 {
217   for(
218       SbHash<const char *, char *>::const_iterator iter =
219        shader_dict->const_begin();
220       iter!=shader_dict->const_end();
221       ++iter
222       ) {
223     delete[] iter->obj;
224   }
225   delete shader_dict;
226 
227   // no need to apply on objects since strings are compiled into the
228   // library and should not be deleted
229   delete shader_builtin_dict;
230 }
231 
232 void
init(void)233 SoShader::init(void)
234 {
235   // Trigger loading and init of Cg library glue.
236   //
237   // FIXME: this function should rather be used from the relevant
238   // class(es), so it is loaded only on demand. 20050125 mortene.
239   (void)cc_cgglue_available();
240 
241   // --- initialization of elements (must be done first) ---------------
242   if (SoGLShaderProgramElement::getClassTypeId() == SoType::badType())
243     SoGLShaderProgramElement::initClass();
244 
245   // --- initialization of shader nodes --------------------------------
246   if (SoShaderProgram::getClassTypeId() == SoType::badType())
247     SoShaderProgram::initClass();
248   if (SoShaderObject::getClassTypeId() == SoType::badType())
249     SoShaderObject::initClass();
250   if (SoFragmentShader::getClassTypeId() == SoType::badType())
251     SoFragmentShader::initClass();
252   if (SoVertexShader::getClassTypeId() == SoType::badType())
253     SoVertexShader::initClass();
254   if (SoGeometryShader::getClassTypeId() == SoType::badType())
255     SoGeometryShader::initClass();
256 
257   // --- initialization of parameter nodes -----------------------------
258   if (SoShaderParameter::getClassTypeId() == SoType::badType())
259     SoShaderParameter::initClass();
260   if (SoUniformShaderParameter::getClassTypeId() == SoType::badType())
261     SoUniformShaderParameter::initClass();
262 
263   // float vector parameter nodes
264   if (SoShaderParameter1f::getClassTypeId() == SoType::badType())
265     SoShaderParameter1f::initClass();
266   if (SoShaderParameter2f::getClassTypeId() == SoType::badType())
267     SoShaderParameter2f::initClass();
268   if (SoShaderParameter3f::getClassTypeId() == SoType::badType())
269     SoShaderParameter3f::initClass();
270   if (SoShaderParameter4f::getClassTypeId() == SoType::badType())
271     SoShaderParameter4f::initClass();
272 
273   // float vector array parameter nodes
274   if (SoShaderParameterArray1f::getClassTypeId() == SoType::badType())
275     SoShaderParameterArray1f::initClass();
276   if (SoShaderParameterArray2f::getClassTypeId() == SoType::badType())
277     SoShaderParameterArray2f::initClass();
278   if (SoShaderParameterArray3f::getClassTypeId() == SoType::badType())
279     SoShaderParameterArray3f::initClass();
280   if (SoShaderParameterArray4f::getClassTypeId() == SoType::badType())
281     SoShaderParameterArray4f::initClass();
282 
283   // matrix parameter nodes
284   if (SoShaderStateMatrixParameter::getClassTypeId() == SoType::badType())
285     SoShaderStateMatrixParameter::initClass();
286   if (SoShaderParameterMatrix::getClassTypeId() == SoType::badType())
287     SoShaderParameterMatrix::initClass();
288   if (SoShaderParameterMatrixArray::getClassTypeId() == SoType::badType())
289     SoShaderParameterMatrixArray::initClass();
290 
291   // int32 support
292   if (SoShaderParameter1i::getClassTypeId() == SoType::badType())
293     SoShaderParameter1i::initClass();
294 
295   // FIXME: Do we need int32 support (like in TGS)? 20040924 martin
296 #if 1
297   if (SoShaderParameter2i::getClassTypeId() == SoType::badType())
298     SoShaderParameter2i::initClass();
299   if (SoShaderParameter3i::getClassTypeId() == SoType::badType())
300     SoShaderParameter3i::initClass();
301   if (SoShaderParameter4i::getClassTypeId() == SoType::badType())
302     SoShaderParameter4i::initClass();
303   if (SoShaderParameterArray1i::getClassTypeId() == SoType::badType())
304     SoShaderParameterArray1i::initClass();
305   if (SoShaderParameterArray2i::getClassTypeId() == SoType::badType())
306     SoShaderParameterArray2i::initClass();
307   if (SoShaderParameterArray3i::getClassTypeId() == SoType::badType())
308     SoShaderParameterArray3i::initClass();
309   if (SoShaderParameterArray4i::getClassTypeId() == SoType::badType())
310     SoShaderParameterArray4i::initClass();
311 #endif
312 
313   SO_SHADER_DIR = coin_getenv("SO_SHADER_DIR");
314   shader_dict = new SbHash<const char *, char *>;
315   shader_builtin_dict = new SbHash<const char *, char *>;
316   setupBuiltinShaders();
317 
318   coin_atexit((coin_atexit_f*) soshader_cleanup, CC_ATEXIT_NORMAL);
319 }
320 
321 
322 
323 
324 const char *
getNamedScript(const SbName & name,const Type type)325 SoShader::getNamedScript(const SbName & name, const Type type)
326 {
327   char * shader = NULL;
328 
329   if (SO_SHADER_DIR) {
330     SbString filename(SO_SHADER_DIR);
331     filename += "/";
332     filename += name.getString();
333 
334     switch (type) {
335     case ARB_SHADER:
336       filename += ".arb";
337       break;
338     case CG_SHADER:
339       filename += ".cg";
340       break;
341     case GLSL_SHADER:
342       filename += ".glsl";
343       break;
344     default:
345       assert(0 && "unknown shader type");
346       break;
347     }
348 
349     SbName shadername(filename.getString());
350 
351     if (!shader_dict->get(shadername.getString(), shader)) {
352       FILE * fp = fopen(filename.getString(), "rb");
353       if (fp) {
354         (void) fseek(fp, 0, SEEK_END);
355         size_t size = (size_t) ftell(fp);
356         (void) fseek(fp, 0, SEEK_SET);
357 
358         shader = new char[size+1];
359         shader[size] = 0;
360         shader_dict->put(shadername, shader);
361 
362         if (!(fread(shader, size, 1, fp) == 1)) {
363           SoDebugError::postWarning("SoShader::getNamedScript",
364                                     "Unable to read shader: %s",
365                                     filename.getString());
366         }
367         fclose(fp);
368       }
369       else {
370         shader_dict->put(shadername, NULL);
371         SoDebugError::postWarning("SoShader::getNamedScript",
372                                   "Unable to find shader: %s",
373                                   filename.getString());
374       }
375     }
376   }
377   if (!shader) {
378     // try builtin shaders
379     if (!shader_builtin_dict->get(name.getString(), shader)) {
380       SoDebugError::postWarning("SoShader::getNamedScript",
381                                 "Unable to find builtin shader: %s",
382                                 name.getString());
383     }
384   }
385 
386   return shader;
387 }
388 
389 void
setupBuiltinShaders(void)390 SoShader::setupBuiltinShaders(void)
391 {
392   shader_builtin_dict->put(SbName("lights/PointLight").getString(), (char*) POINTLIGHT_shadersource);
393   shader_builtin_dict->put(SbName("lights/SpotLight").getString(), (char*) SPOTLIGHT_shadersource);
394   shader_builtin_dict->put(SbName("lights/DirectionalLight").getString(), (char*) DIRECTIONALLIGHT_shadersource);
395   shader_builtin_dict->put(SbName("lights/DirSpotLight").getString(), (char*) DIRSPOTLIGHT_shadersource);
396   shader_builtin_dict->put(SbName("vsm/VsmLookup").getString(), (char*) VSMLOOKUP_shadersource);
397 }
398