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   \class SoShaderObject SoShaderObject.h Inventor/nodes/SoShaderObject.h
35   \brief The SoShaderObject class is the superclass for all shader classes in Coin.
36 
37   See \ref coin_shaders "Shaders in Coin" for more information
38   on how to set up a scene graph with shaders.
39 
40   \ingroup shaders
41 
42   \sa SoShaderProgram
43 */
44 
45 /*!
46   \var SoSFBool SoShaderObject::isActive
47 
48   Enabled/disables the shader. Default value is TRUE.
49 */
50 
51 /*!
52   \var SoSFString SoShaderObject::sourceProgram
53 
54   The shader program, or a file name if the shader should be loaded from a file.
55   If the shader is loaded from a file, the shader type is identified by the
56   file extension. .glsl for GLSL shaders, .cg for Cg shaders, and .vp and .fp
57   for ARB shaders.
58 */
59 
60 
61 /*!
62   \var SoSFEnum SoShaderObject::sourceType
63 
64   The type of shader.
65 */
66 
67 
68 /*!
69   \enum SoShaderObject::SourceType
70 
71   Used for enumerating the shader types in sourceProgram.
72 */
73 
74 /*!
75   \var SoShaderObject::SourceType SoShaderObject::ARB_PROGRAM
76 
77   Specifies an ARB shader.
78 */
79 
80 /*!
81   \var SoShaderObject::SourceType SoShaderObject::CG_PROGRAM
82 
83   Specifies a Cg shader program.
84 */
85 
86 /*!
87   \var SoShaderObject::SourceType SoShaderObject::GLSL_PROGRAM
88 
89   Specifies a GLSL program.
90 */
91 
92 /*!
93   \var SoShaderObject::SourceType SoShaderObject::FILENAME
94 
95   Shader should be loaded from the file in sourceProgram.
96 */
97 
98 
99 /*!
100   \var SoMFNode SoShaderObject::parameter
101 
102   The shader program parameters.
103 */
104 
105 #include <Inventor/nodes/SoShaderObject.h>
106 
107 #include <cassert>
108 
109 #include <Inventor/actions/SoGLRenderAction.h>
110 #include <Inventor/actions/SoSearchAction.h>
111 #include <Inventor/elements/SoGLCacheContextElement.h>
112 #include <Inventor/elements/SoGLShaderProgramElement.h>
113 #include <Inventor/elements/SoGLMultiTextureImageElement.h>
114 #include <Inventor/elements/SoLazyElement.h>
115 #include <Inventor/misc/SoContextHandler.h>
116 #include <Inventor/misc/SoGLDriverDatabase.h>
117 #include <Inventor/errors/SoDebugError.h>
118 #include <Inventor/nodes/SoFragmentShader.h>
119 #include <Inventor/nodes/SoShaderParameter.h>
120 #include <Inventor/nodes/SoVertexShader.h>
121 #include <Inventor/nodes/SoGeometryShader.h>
122 #include <Inventor/sensors/SoNodeSensor.h>
123 #include <Inventor/SoInput.h>
124 #include <Inventor/lists/SbStringList.h>
125 
126 #include "nodes/SoSubNodeP.h"
127 #include "misc/SbHash.h"
128 #include "shaders/SoGLARBShaderObject.h"
129 #include "shaders/SoGLCgShaderObject.h"
130 #include "shaders/SoGLSLShaderObject.h"
131 #include "shaders/SoGLShaderProgram.h"
132 
133 // *************************************************************************
134 
135 class SoShaderObjectP
136 {
137 public:
138   SoShaderObjectP(SoShaderObject *ownerptr);
139   ~SoShaderObjectP();
140 
141   void GLRender(SoGLRenderAction *action);
142 
getGLShaderObject(const uint32_t cachecontext)143   SoGLShaderObject * getGLShaderObject(const uint32_t cachecontext) {
144     SoGLShaderObject * obj = NULL;
145     if (this->glshaderobjects.get(cachecontext, obj)) return obj;
146     return NULL;
147   }
setGLShaderObject(SoGLShaderObject * obj,const uint32_t cachecontext)148   void setGLShaderObject(SoGLShaderObject * obj, const uint32_t cachecontext) {
149     SoGLShaderObject * oldshader;
150     if (this->glshaderobjects.get(cachecontext, oldshader)) {
151       SoGLCacheContextElement::scheduleDeleteCallback(oldshader->getCacheContext(),
152                                                       really_delete_object, oldshader);
153     }
154     (void) this->glshaderobjects.put(cachecontext, obj);
155   }
deleteGLShaderObjects(void)156   void deleteGLShaderObjects(void) {
157     SbList <uint32_t> keylist;
158     this->glshaderobjects.makeKeyList(keylist);
159     for (int i = 0; i < keylist.getLength(); i++) {
160       SoGLShaderObject * glshader = NULL;
161       (void) this->glshaderobjects.get(keylist[i], glshader);
162       SoGLCacheContextElement::scheduleDeleteCallback(glshader->getCacheContext(),
163                                                       really_delete_object, glshader);
164     }
165     this->glshaderobjects.clear();
166   }
167   //
168   // Callback from SoGLCacheContextElement
169   //
really_delete_object(void * closure,uint32_t COIN_UNUSED_ARG (contextid))170   static void really_delete_object(void * closure, uint32_t COIN_UNUSED_ARG(contextid)) {
171     SoGLShaderObject * obj = (SoGLShaderObject*) closure;
172     delete obj;
173   }
174   //
175   // callback from SoContextHandler
176   //
context_destruction_cb(uint32_t cachecontext,void * userdata)177   static void context_destruction_cb(uint32_t cachecontext, void * userdata) {
178     SoShaderObjectP * thisp = (SoShaderObjectP*) userdata;
179 
180     SoGLShaderObject * oldshader;
181     if (thisp->glshaderobjects.get(cachecontext, oldshader)) {
182       // just delete immediately. The context is current
183       delete oldshader;
184       thisp->glshaderobjects.erase(cachecontext);
185     }
186   }
187 
invalidateParameters(void)188   void invalidateParameters(void) {
189     SbList <uint32_t> keylist;
190     this->glshaderobjects.makeKeyList(keylist);
191     for (int i = 0; i < keylist.getLength(); i++) {
192       SoGLShaderObject * glshader = NULL;
193       (void) this->glshaderobjects.get(keylist[i], glshader);
194       glshader->setParametersDirty(TRUE);
195     }
196   }
197 
198   SoShaderObject * owner;
199   SoShaderObject::SourceType cachedSourceType;
200   SbString cachedSourceProgram;
201   SbBool didSetSearchDirectories;
202   SbBool shouldload;
203   SoNodeSensor *sensor;
204 
205   void updateParameters(const uint32_t cachecontext, int start, int num);
206   void updateAllParameters(const uint32_t cachecontext);
207   void updateCoinParameters(const uint32_t cachecontext, SoState * state);
208   void updateStateMatrixParameters(const uint32_t cachecontext, SoState * state);
209   SbBool containStateMatrixParameters(void) const;
210   void setSearchDirectories(const SbStringList & list);
211 
212 private:
213   static void sensorCB(void *data, SoSensor *);
214 
215   SbStringList searchdirectories;
216   SbHash<uint32_t, SoGLShaderObject *> glshaderobjects;
217 
218   void checkType(void); // sets cachedSourceType
219   void readSource(void); // sets cachedSourceProgram depending on sourceType
220 
221   SbBool isSupported(SoShaderObject::SourceType sourceType, const cc_glglue * glue);
222 
223 #if defined(SOURCE_HINT)
224   SbString getSourceHint(void) const;
225 #endif
226 };
227 
228 #define PRIVATE(obj) ((obj)->pimpl)
229 
230 // *************************************************************************
231 
232 SO_NODE_ABSTRACT_SOURCE(SoShaderObject);
233 
234 // *************************************************************************
235 
236 // doc from parent
initClass(void)237 void SoShaderObject::initClass(void)
238 {
239   SO_NODE_INTERNAL_INIT_ABSTRACT_CLASS(SoShaderObject,
240                                        SO_FROM_COIN_2_5|SO_FROM_INVENTOR_5_0);
241 }
242 
243 /*!
244   Constructor.
245 */
SoShaderObject(void)246 SoShaderObject::SoShaderObject(void)
247 {
248   SO_NODE_INTERNAL_CONSTRUCTOR(SoShaderObject);
249 
250   SO_NODE_ADD_FIELD(isActive, (TRUE));
251 
252   SO_NODE_DEFINE_ENUM_VALUE(SourceType, ARB_PROGRAM);
253   SO_NODE_DEFINE_ENUM_VALUE(SourceType, CG_PROGRAM);
254   SO_NODE_DEFINE_ENUM_VALUE(SourceType, GLSL_PROGRAM);
255   SO_NODE_DEFINE_ENUM_VALUE(SourceType, FILENAME);
256 
257   SO_NODE_ADD_FIELD(sourceType, (FILENAME));
258   SO_NODE_SET_SF_ENUM_TYPE(sourceType, SourceType);
259 
260   SO_NODE_ADD_FIELD(sourceProgram, (""));
261   SO_NODE_ADD_FIELD(parameter, (NULL));
262   this->parameter.setNum(0);
263   this->parameter.setDefault(TRUE);
264 
265   PRIVATE(this) = new SoShaderObjectP(this);
266 }
267 
268 /*!
269   Destructor
270 */
~SoShaderObject()271 SoShaderObject::~SoShaderObject()
272 {
273   delete PRIVATE(this);
274 }
275 
276 // doc from parent
277 void
GLRender(SoGLRenderAction * action)278 SoShaderObject::GLRender(SoGLRenderAction * action)
279 {
280   PRIVATE(this)->GLRender(action);
281 }
282 
283 // doc from parent
284 void
search(SoSearchAction * action)285 SoShaderObject::search(SoSearchAction * action)
286 {
287   // Include this node in the search.
288   SoNode::search(action);
289   if (action->isFound()) return;
290 
291   // we really can't do this since this node hasn't got an SoChildList
292   // instance
293 #if 0 // disabled, not possible to search under this node
294   int numindices;
295   const int * indices;
296   if (action->getPathCode(numindices, indices) == SoAction::IN_PATH) {
297     // FIXME: not implemented -- 20050129 martin
298   }
299   else { // traverse all shader parameter
300     int num = this->parameter.getNum();
301     for (int i=0; i<num; i++) {
302       SoNode * node = this->parameter[i];
303       action->pushCurPath(i, node);
304       SoNodeProfiling profiling;
305       profiling.preTraversal(action);
306       node->search(action);
307       profiling.postTraversal(action);
308       action->popCurPath();
309       if (action->isFound()) return;
310     }
311   }
312 #endif // disabled
313 }
314 
315 // doc from parent
316 SbBool
readInstance(SoInput * in,unsigned short flags)317 SoShaderObject::readInstance(SoInput * in, unsigned short flags)
318 {
319   PRIVATE(this)->sensor->detach();
320   PRIVATE(this)->deleteGLShaderObjects();
321 
322   SbBool ret = inherited::readInstance(in, flags);
323   if (ret) {
324     PRIVATE(this)->setSearchDirectories(SoInput::getDirectories());
325   }
326   PRIVATE(this)->sensor->attach(this);
327 
328   return ret;
329 }
330 
331 /*!
332   Returns the shader type detected in sourceProgram.
333 */
334 SoShaderObject::SourceType
getSourceType(void) const335 SoShaderObject::getSourceType(void) const
336 {
337   return PRIVATE(this)->cachedSourceType;
338 }
339 
340 /*!
341   Returns the actual shader program.
342 */
getSourceProgram(void) const343 SbString SoShaderObject::getSourceProgram(void) const
344 {
345   return PRIVATE(this)->cachedSourceProgram;
346 }
347 
348 /*!
349   Used internally to update shader paramters.
350 */
351 void
updateParameters(SoState * state)352 SoShaderObject::updateParameters(SoState * state)
353 {
354   const uint32_t cachecontext = SoGLCacheContextElement::get(state);
355   PRIVATE(this)->updateAllParameters(cachecontext);
356   PRIVATE(this)->updateStateMatrixParameters(cachecontext, state);
357   PRIVATE(this)->updateCoinParameters(cachecontext, state);
358 }
359 
360 /* ***************************************************************************
361  * *** private implementation of SoShaderObjectP ***
362  * ***************************************************************************/
363 
SoShaderObjectP(SoShaderObject * ownerptr)364 SoShaderObjectP::SoShaderObjectP(SoShaderObject * ownerptr)
365 {
366   this->owner = ownerptr;
367   this->sensor = new SoNodeSensor(SoShaderObjectP::sensorCB, this);
368   this->sensor->setPriority(0);
369   this->sensor->attach(ownerptr);
370 
371   this->cachedSourceType = SoShaderObject::FILENAME;
372   this->didSetSearchDirectories = FALSE;
373   this->shouldload = TRUE;
374 
375   SoContextHandler::addContextDestructionCallback(context_destruction_cb, this);
376 }
377 
~SoShaderObjectP()378 SoShaderObjectP::~SoShaderObjectP()
379 {
380   SoContextHandler::removeContextDestructionCallback(context_destruction_cb, this);
381 
382   this->deleteGLShaderObjects();
383 
384   SbStringList empty;
385   this->setSearchDirectories(empty);
386   delete this->sensor;
387 }
388 
389 void
GLRender(SoGLRenderAction * action)390 SoShaderObjectP::GLRender(SoGLRenderAction * action)
391 {
392   SbBool isactive = this->owner->isActive.getValue();
393   if (!isactive) return;
394 
395   SoState * state = action->getState();
396 
397   SoGLShaderProgram * shaderProgram = SoGLShaderProgramElement::get(state);
398   if (!shaderProgram) {
399     SoDebugError::postWarning("SoShaderObject::GLRender",
400                               "SoShaderObject seems to not be under a SoShaderProgram node");
401     return;
402   }
403 
404   const uint32_t cachecontext = SoGLCacheContextElement::get(state);
405   const cc_glglue * glue = cc_glglue_instance(cachecontext);
406 
407   SoGLShaderObject * shaderobject = this->getGLShaderObject(cachecontext);
408 
409   if (this->owner->sourceProgram.isDefault() ||
410       this->owner->sourceProgram.getValue().getLength() == 0) { return; }
411 
412   if (shaderobject == NULL) {
413     if (this->shouldload) {
414       this->checkType(); // set this->cachedSourceType
415       this->readSource(); // set this->cachedSourceProgram
416       this->shouldload = FALSE;
417     }
418     // if file could not be read
419     if (this->cachedSourceType == SoShaderObject::FILENAME) return;
420 
421     if (!this->isSupported(this->cachedSourceType, glue)) {
422       SbString s;
423       switch (this->cachedSourceType) {
424       case SoShaderObject::ARB_PROGRAM: s = "ARB_PROGRAM"; break;
425       case SoShaderObject::CG_PROGRAM: s = "CG_PROGRAM"; break;
426       case SoShaderObject::GLSL_PROGRAM: s = "GLSL_PROGRAM"; break;
427       default: assert(FALSE && "unknown shader");
428       }
429       SoDebugError::postWarning("SoShaderObjectP::GLRender",
430                                 "%s is not supported", s.getString());
431       return;
432     }
433 
434     switch (this->cachedSourceType) {
435     case SoShaderObject::ARB_PROGRAM:
436       shaderobject = new SoGLARBShaderObject(cachecontext);
437       break;
438     case SoShaderObject::CG_PROGRAM:
439       shaderobject = new SoGLCgShaderObject(cachecontext);
440       break;
441     case SoShaderObject::GLSL_PROGRAM:
442       shaderobject = new SoGLSLShaderObject(cachecontext);
443       break;
444     default:
445       assert(FALSE && "This shouldn't happen!");
446     }
447 
448     if (this->owner->isOfType(SoVertexShader::getClassTypeId())) {
449       shaderobject->setShaderType(SoGLShaderObject::VERTEX);
450     }
451     else if (this->owner->isOfType(SoFragmentShader::getClassTypeId())) {
452       shaderobject->setShaderType(SoGLShaderObject::FRAGMENT);
453     }
454     else {
455       assert(this->owner->isOfType(SoGeometryShader::getClassTypeId()));
456       shaderobject->setShaderType(SoGLShaderObject::GEOMETRY);
457 
458       //SoGeometryShader * geomshader = (SoGeometryShader*) this->owner;
459 
460     }
461 
462 #if defined(SOURCE_HINT)
463     shaderobject->sourceHint = getSourceHint();
464 #endif
465     shaderobject->load(this->cachedSourceProgram.getString());
466     this->setGLShaderObject(shaderobject, cachecontext);
467   }
468   if (shaderobject) {
469     shaderProgram->addShaderObject(shaderobject);
470     shaderobject->setIsActive(isactive);
471   }
472 }
473 
474 // sets this->cachedSourceType to [ARB|CG|GLSL]_PROGRAM
475 // this->cachedSourceType will be set to FILENAME, if sourceType is unknown
476 void
checkType(void)477 SoShaderObjectP::checkType(void)
478 {
479   this->cachedSourceType =
480     (SoShaderObject::SourceType)this->owner->sourceType.getValue();
481 
482   if (this->cachedSourceType != SoShaderObject::FILENAME) return;
483 
484   // determine sourceType from file extension
485   SbString fileName = this->owner->sourceProgram.getValue();
486   int len = fileName.getLength();
487   if (len > 5) {
488     SbString subStr = fileName.getSubString(len-5);
489     if (subStr == ".glsl" || subStr == ".vert" || subStr == ".frag") {
490       this->cachedSourceType = SoShaderObject::GLSL_PROGRAM;
491       return;
492     }
493   }
494   if (len > 3) {
495     SbString subStr = fileName.getSubString(len-3);
496     if (subStr == ".cg") {
497       this->cachedSourceType = SoShaderObject::CG_PROGRAM;
498       return;
499     }
500     if (subStr == ".fp") {
501       this->cachedSourceType = this->owner->isOfType(SoVertexShader::getClassTypeId())
502         ? SoShaderObject::FILENAME : SoShaderObject::ARB_PROGRAM;
503       return;
504     }
505     if (subStr==".vp") {
506       this->cachedSourceType = this->owner->isOfType(SoVertexShader::getClassTypeId())
507         ? SoShaderObject::ARB_PROGRAM : SoShaderObject::FILENAME;
508       return;
509     }
510   }
511   SoDebugError::postWarning("SoShaderObjectP::checkType",
512                             "Could not determine shader type of file '%s'!\n"
513                             "Following file extensions are supported:\n"
514                             "*.fp -> ARB_PROGRAM (fragment)\n"
515                             "*.vp -> ARB_PROGRAM (vertex)\n"
516                             "*.cg -> CG_PROGRAM (fragment|vertex)\n"
517                             "*.glsl *.vert *.frag -> GLSL_PROGRAM (fragment|vertex)\n",
518                             fileName.getString());
519   // error: could not determine SourceType
520   this->cachedSourceType = SoShaderObject::FILENAME;
521 }
522 
523 // read the file if neccessary and assign content to this->cachedSourceProgram
524 void
readSource(void)525 SoShaderObjectP::readSource(void)
526 {
527   SoShaderObject::SourceType srcType =
528     (SoShaderObject::SourceType)this->owner->sourceType.getValue();
529 
530   this->cachedSourceProgram.makeEmpty();
531 
532   if (this->owner->sourceProgram.isDefault())
533     return;
534   else if (srcType != SoShaderObject::FILENAME)
535     this->cachedSourceProgram = this->owner->sourceProgram.getValue();
536   else {
537     if (this->cachedSourceType != SoShaderObject::FILENAME) {
538 
539       SbStringList subdirs;
540       subdirs.append(new SbString("shader"));
541       subdirs.append(new SbString("shaders"));
542       SbString fileName = SoInput::searchForFile(this->owner->sourceProgram.getValue(),
543                                                  this->searchdirectories,
544                                                  subdirs);
545       // delete allocated subdirs before continuing
546       delete subdirs[0];
547       delete subdirs[1];
548 
549       if (fileName.getLength() <= 0) {
550         SoDebugError::postWarning("SoShaderObjectP::readSource",
551                                   "Shader file not found: '%s'",
552                                   this->owner->sourceProgram.getValue().getString());
553         this->cachedSourceType = SoShaderObject::FILENAME;
554         return;
555       }
556 
557       FILE * f = fopen(fileName.getString(), "rb");
558       SbBool readok = FALSE;
559       if (f) {
560         if (fseek(f, 0L, SEEK_END) == 0) {
561           const long length = ftell(f);
562           if ((length > 0) && (fseek(f, 0L, SEEK_SET) == 0)) {
563             char * srcstr = new char[length+1];
564             size_t readlen = fread(srcstr, 1, length, f);
565             if (readlen == (size_t) length) {
566               srcstr[length] = '\0';
567               this->cachedSourceProgram = srcstr;
568               readok = TRUE;
569             }
570             delete[] srcstr;
571           }
572         }
573         fclose(f);
574       }
575       if (!readok) {
576         this->cachedSourceType = SoShaderObject::FILENAME;
577         SoDebugError::postWarning("SoShaderObjectP::readSource",
578                                   "Could not read shader file '%s'",
579                                   fileName.getString());
580       }
581     }
582   }
583 }
584 
585 SbBool
isSupported(SoShaderObject::SourceType sourceType,const cc_glglue * glue)586 SoShaderObjectP::isSupported(SoShaderObject::SourceType sourceType, const cc_glglue * glue)
587 {
588   if (this->owner->isOfType(SoVertexShader::getClassTypeId())) {
589     // don't call this function. It's not context safe. pederb, 20051103
590     // return SoVertexShader::isSupported(sourceType);
591 
592     if (sourceType == SoShaderObject::ARB_PROGRAM) {
593       return SoGLDriverDatabase::isSupported(glue, SO_GL_ARB_VERTEX_PROGRAM);
594     }
595     else if (sourceType == SoShaderObject::GLSL_PROGRAM) {
596       return SoGLDriverDatabase::isSupported(glue, SO_GL_ARB_SHADER_OBJECT);
597     }
598     // FIXME: Add support for detecting missing Cg support
599     // (20050427 handegar)
600     else if (sourceType == SoShaderObject::CG_PROGRAM) return TRUE;
601     return FALSE;
602   }
603   else if (this->owner->isOfType(SoFragmentShader::getClassTypeId())) {
604     // don't call this function. It's not context safe. pederb, 20051103
605     // return SoFragmentShader::isSupported(sourceType);
606 
607     if (sourceType == SoShaderObject::ARB_PROGRAM) {
608       return SoGLDriverDatabase::isSupported(glue, SO_GL_ARB_FRAGMENT_PROGRAM);
609     }
610     else if (sourceType == SoShaderObject::GLSL_PROGRAM) {
611       return SoGLDriverDatabase::isSupported(glue, SO_GL_ARB_SHADER_OBJECT);
612     }
613     // FIXME: Add support for detecting missing Cg support (20050427
614     // handegar)
615     else if (sourceType == SoShaderObject::CG_PROGRAM) return TRUE;
616     return FALSE;
617   }
618   else {
619     assert(this->owner->isOfType(SoGeometryShader::getClassTypeId()));
620     if (sourceType == SoShaderObject::GLSL_PROGRAM) {
621       return
622         SoGLDriverDatabase::isSupported(glue, "GL_EXT_geometry_shader4") &&
623         SoGLDriverDatabase::isSupported(glue, SO_GL_ARB_SHADER_OBJECT);
624     }
625     return FALSE;
626   }
627 }
628 
629 void
updateParameters(const uint32_t cachecontext,int start,int num)630 SoShaderObjectP::updateParameters(const uint32_t cachecontext, int start, int num)
631 {
632 
633   if (!this->owner->isActive.getValue()) return;
634   if (start < 0 || num < 0) return;
635 
636   SoGLShaderObject * shaderobject = this->getGLShaderObject(cachecontext);
637   if ((shaderobject == NULL) || !shaderobject->getParametersDirty()) return;
638 
639   int cnt = this->owner->parameter.getNum();
640   int end = start+num;
641 
642   end = (end > cnt) ? cnt : end;
643   for (int i=start; i<end; i++) {
644     SoUniformShaderParameter * param =
645       (SoUniformShaderParameter*)this->owner->parameter[i];
646     param->updateParameter(shaderobject);
647   }
648 }
649 
650 void
updateCoinParameters(const uint32_t cachecontext,SoState * state)651 SoShaderObjectP::updateCoinParameters(const uint32_t cachecontext, SoState * state)
652 {
653   int i, cnt = this->owner->parameter.getNum();
654 
655   SoGLShaderObject * shaderobject = this->getGLShaderObject(cachecontext);
656 
657   for (i = 0; i < cnt; i++) {
658     SoUniformShaderParameter * param =
659       (SoUniformShaderParameter*)this->owner->parameter[i];
660     SbName name = param->name.getValue();
661 
662     if (strncmp(name.getString(), "coin_", 5) == 0) {
663       if (name == "coin_texunit0_model") {
664         SoMultiTextureImageElement::Model model;
665         SbColor dummy;
666         SbBool tex = SoGLMultiTextureImageElement::get(state, model, dummy) != NULL;
667         shaderobject->updateCoinParameter(state, name, NULL, tex ? model : 0);
668       }
669       else if (name == "coin_texunit1_model") {
670         SoMultiTextureImageElement::Model model;
671         SbColor dummy;
672         SbBool tex = SoGLMultiTextureImageElement::get(state, 1, model, dummy) != NULL;
673         shaderobject->updateCoinParameter(state, name, NULL, tex ? model : 0);
674       }
675       else if (name == "coin_texunit2_model") {
676         SoMultiTextureImageElement::Model model;
677         SbColor dummy;
678         SbBool tex = SoGLMultiTextureImageElement::get(state, 2, model, dummy) != NULL;
679         shaderobject->updateCoinParameter(state, name, NULL, tex ? model : 0);
680       }
681       else if (name == "coin_texunit3_model") {
682         SoMultiTextureImageElement::Model model;
683         SbColor dummy;
684         SbBool tex = SoGLMultiTextureImageElement::get(state, 3, model, dummy) != NULL;
685         shaderobject->updateCoinParameter(state, name, NULL, tex ? model : 0);
686       }
687       else if (name == "coin_light_model") {
688         shaderobject->updateCoinParameter(state, name, NULL, SoLazyElement::getLightModel(state));
689       }
690       else if (name == "coin_two_sided_lighting") {
691         shaderobject->updateCoinParameter(state, name, NULL, SoLazyElement::getTwoSidedLighting(state));
692       }
693     }
694   }
695 }
696 
697 
698 void
updateAllParameters(const uint32_t cachecontext)699 SoShaderObjectP::updateAllParameters(const uint32_t cachecontext)
700 {
701   if (!this->owner->isActive.getValue()) return;
702 
703   SoGLShaderObject * shaderobject = this->getGLShaderObject(cachecontext);
704   if ((shaderobject == NULL) || !shaderobject->getParametersDirty()) return;
705 
706   int i, cnt = this->owner->parameter.getNum();
707 
708   for (i=0; i<cnt; i++) {
709     SoUniformShaderParameter *param =
710       (SoUniformShaderParameter*)this->owner->parameter[i];
711     param->updateParameter(shaderobject);
712   }
713   shaderobject->setParametersDirty(FALSE);
714 }
715 
716 // Update state matrix paramaters
717 void
updateStateMatrixParameters(const uint32_t cachecontext,SoState * state)718 SoShaderObjectP::updateStateMatrixParameters(const uint32_t cachecontext, SoState *state)
719 {
720 #define STATE_PARAM SoShaderStateMatrixParameter
721   if (!this->owner->isActive.getValue()) return;
722 
723   SoGLShaderObject * shaderobject = this->getGLShaderObject(cachecontext);
724   if (shaderobject == NULL) return;
725 
726   int i, cnt = this->owner->parameter.getNum();
727   for (i= 0; i <cnt; i++) {
728     STATE_PARAM * param = (STATE_PARAM*)this->owner->parameter[i];
729     if (param->isOfType(STATE_PARAM::getClassTypeId())) {
730       param->updateValue(state);
731       param->updateParameter(shaderobject);
732 	}
733   }
734 #undef STATE_PARAM
735 }
736 
737 SbBool
containStateMatrixParameters(void) const738 SoShaderObjectP::containStateMatrixParameters(void) const
739 {
740 #define STATE_PARAM SoShaderStateMatrixParameter
741   int i, cnt = this->owner->parameter.getNum();
742   for (i = 0; i < cnt; i++) {
743     if (this->owner->parameter[i]->isOfType(STATE_PARAM::getClassTypeId()))
744       return TRUE;
745   }
746 #undef STATE_PARAM
747   return FALSE;
748 }
749 
750 #if defined(SOURCE_HINT)
751 SbString
getSourceHint(void) const752 SoShaderObjectP::getSourceHint(void) const
753 {
754   SoShaderObject::SourceType srcType =
755     (SoShaderObject::SourceType)this->owner->sourceType.getValue();
756 
757   if (srcType == SoShaderObject::FILENAME)
758     return this->owner->sourceProgram.getValue();
759   else
760     return ""; // FIXME: should return first line of shader source code
761 }
762 #endif
763 
764 void
sensorCB(void * data,SoSensor * sensor)765 SoShaderObjectP::sensorCB(void *data, SoSensor *sensor)
766 {
767   SoShaderObjectP * thisp = (SoShaderObjectP*) data;
768   SoField * field = ((SoNodeSensor *)sensor)->getTriggerField();
769 
770   if (field == &thisp->owner->sourceProgram ||
771       field == &thisp->owner->sourceType) {
772     thisp->deleteGLShaderObjects();
773     thisp->shouldload = TRUE;
774   }
775   else if (field == &thisp->owner->parameter) {
776     thisp->invalidateParameters();
777   }
778   if (!thisp->didSetSearchDirectories) {
779     thisp->setSearchDirectories(SoInput::getDirectories());
780   }
781 }
782 
783 void
setSearchDirectories(const SbStringList & list)784 SoShaderObjectP::setSearchDirectories(const SbStringList & list)
785 {
786   int i;
787   for (i = 0; i< this->searchdirectories.getLength(); i++) {
788     delete this->searchdirectories[i];
789   }
790 
791   for (i = 0; i < list.getLength(); i++) {
792     this->searchdirectories.append(new SbString(*(list[i])));
793   }
794   this->didSetSearchDirectories = TRUE;
795 }
796 
797 #undef PRIVATE
798