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 SoGLMultiTextureCoordinateElement Inventor/elements/SoGLMultiTextureCoordinateElement.h
35   \brief The SoGLMultiTextureCoordinateElement class stores the current gltexture coordinates for several units.
36 
37   \ingroup elements
38 */
39 
40 /*! \file SoGLMultiTextureCoordinateElement.h */
41 #include <Inventor/elements/SoGLMultiTextureCoordinateElement.h>
42 #include <Inventor/elements/SoMultiTextureEnabledElement.h>
43 #include <Inventor/actions/SoGLRenderAction.h>
44 #include <Inventor/misc/SoState.h>
45 #include <Inventor/lists/SbList.h>
46 
47 #include <cassert>
48 
49 #ifdef HAVE_CONFIG_H
50 #include <config.h>
51 #endif // HAVE_CONFIG_H
52 
53 #include <Inventor/system/gl.h>
54 #include <Inventor/C/glue/gl.h>
55 
56 class SoGLMultiTextureCoordinateElementP {
57 public:
58   mutable SbList<SoGLMultiTextureCoordinateElement::GLUnitData> unitdata;
59   int contextid;
60 
61   // switch/case table for faster rendering.
62   enum SendLookup {
63     UNINITIALIZED,
64     NONE,
65     FUNCTION,
66     TEXCOORD2,
67     TEXCOORD3,
68     TEXCOORD4
69   };
70   mutable SbList<SendLookup> sendlookup;
71   const cc_glglue * glue;
72   SoGLMultiTextureCoordinateElement::GLUnitData defaultdata;
ensureCapacity(int unit) const73   void ensureCapacity(int unit) const {
74     while (unit >= this->unitdata.getLength()) {
75       this->unitdata.append(SoGLMultiTextureCoordinateElement::GLUnitData());
76     }
77   }
78 
79 };
80 
81 #define PRIVATE(obj) obj->pimpl
82 
83 SO_ELEMENT_CUSTOM_CONSTRUCTOR_SOURCE(SoGLMultiTextureCoordinateElement);
84 
85 /*!
86   This static method initializes static data for the
87   SoGLMultiTextureCoordinateElement class.
88 */
89 
90 void
initClass(void)91 SoGLMultiTextureCoordinateElement::initClass(void)
92 {
93   SO_ELEMENT_INIT_CLASS(SoGLMultiTextureCoordinateElement, inherited);
94 }
95 
96 /*!
97   The constructor.
98 */
SoGLMultiTextureCoordinateElement(void)99 SoGLMultiTextureCoordinateElement::SoGLMultiTextureCoordinateElement(void)
100 {
101   PRIVATE(this) = new SoGLMultiTextureCoordinateElementP;
102 
103   this->setTypeId(SoGLMultiTextureCoordinateElement::classTypeId);
104   this->setStackIndex(SoGLMultiTextureCoordinateElement::classStackIndex);
105 }
106 
107 /*!
108   The destructor.
109 */
110 
~SoGLMultiTextureCoordinateElement()111 SoGLMultiTextureCoordinateElement::~SoGLMultiTextureCoordinateElement()
112 {
113   delete PRIVATE(this);
114 }
115 
116 //!  FIXME: write doc.
117 
118 void
init(SoState * state)119 SoGLMultiTextureCoordinateElement::init(SoState * state)
120 {
121   SoAction * action = state->getAction();
122   assert(action->isOfType(SoGLRenderAction::getClassTypeId()));
123   // fetch cache context id from action since SoGLCacheContextElement
124   // might not be initialized yet.
125   SoGLRenderAction * glaction = (SoGLRenderAction*) action;
126   PRIVATE(this)->contextid = glaction->getCacheContext();
127 
128   inherited::init(state);
129   PRIVATE(this)->unitdata.truncate(0);
130   PRIVATE(this)->sendlookup.truncate(0);
131 }
132 
133 //!  FIXME: write doc.
134 
135 void
push(SoState * state)136 SoGLMultiTextureCoordinateElement::push(SoState * state)
137 {
138   inherited::push(state);
139   SoGLMultiTextureCoordinateElement * prev = (SoGLMultiTextureCoordinateElement*)this->getNextInStack();
140 
141   PRIVATE(this)->contextid = PRIVATE(prev)->contextid;
142   PRIVATE(this)->unitdata = PRIVATE(prev)->unitdata;
143 
144   prev->capture(state);
145 }
146 
147 //!  FIXME: write doc.
148 
149 void
pop(SoState * state,const SoElement * prevTopElement)150 SoGLMultiTextureCoordinateElement::pop(SoState * state,
151                                        const SoElement * prevTopElement)
152 {
153   inherited::pop(state, prevTopElement);
154   SoGLMultiTextureCoordinateElement * prev = (SoGLMultiTextureCoordinateElement*) prevTopElement;
155 
156   const cc_glglue * glue = cc_glglue_instance(PRIVATE(this)->contextid);
157   const int maxunits = SbMax(PRIVATE(this)->unitdata.getLength(),
158                              PRIVATE(prev)->unitdata.getLength());
159 
160   for (int i = 0; i < maxunits; i++) {
161     const GLUnitData & thisud =
162       i < PRIVATE(this)->unitdata.getLength() ?
163       PRIVATE(this)->unitdata[i] : PRIVATE(this)->defaultdata;
164     const GLUnitData & prevud =
165       i < PRIVATE(prev)->unitdata.getLength() ?
166       PRIVATE(prev)->unitdata[i] : PRIVATE(prev)->defaultdata;
167 
168     SbBool enablegen = FALSE;
169     SbBool disablegen = FALSE;
170     SbBool docallback = FALSE;
171 
172     if (thisud.texgenCB && !prevud.texgenCB) {enablegen = TRUE; docallback = TRUE;}
173     else if (!thisud.texgenCB && prevud.texgenCB) disablegen = TRUE;
174     else if (thisud.texgenCB/* != prevud.texgenCB*/) docallback = TRUE;
175 
176 /*
177   See the comments in the setElt function below for the explanation for commenting
178   out the second half of the above else if statement. RHW
179 */
180 
181 	if (enablegen || disablegen || docallback) {
182       // must change texture unit while updating OpenGL
183       cc_glglue_glActiveTexture(glue, (GLenum) (int(GL_TEXTURE0) + i));
184     }
185     if (enablegen) {
186       glEnable(GL_TEXTURE_GEN_S);
187       glEnable(GL_TEXTURE_GEN_T);
188       glEnable(GL_TEXTURE_GEN_R);
189       glEnable(GL_TEXTURE_GEN_Q);
190     }
191     if (disablegen) {
192       glDisable(GL_TEXTURE_GEN_S);
193       glDisable(GL_TEXTURE_GEN_T);
194       glDisable(GL_TEXTURE_GEN_R);
195       glDisable(GL_TEXTURE_GEN_Q);
196     }
197     if (docallback) {
198       this->doCallback(i);
199     }
200     // restore default unit
201     if (enablegen || disablegen || docallback) {
202       cc_glglue_glActiveTexture(glue, (GLenum) GL_TEXTURE0);
203     }
204   }
205 }
206 
207 //!  FIXME: write doc.
208 
209 void
setTexGen(SoState * const state,SoNode * const node,const int unit,SoTexCoordTexgenCB * const texgenFunc,void * const texgenData,SoTextureCoordinateFunctionCB * const func,void * const funcData)210 SoGLMultiTextureCoordinateElement::setTexGen(SoState * const state,
211                                              SoNode * const node,
212                                              const int unit,
213                                              SoTexCoordTexgenCB * const texgenFunc,
214                                              void * const texgenData,
215                                              SoTextureCoordinateFunctionCB * const func,
216                                              void * const funcData)
217 {
218   SoMultiTextureCoordinateElement::setFunction(state, node, unit, func, funcData);
219 
220   SoGLMultiTextureCoordinateElement *element = (SoGLMultiTextureCoordinateElement *)
221     SoElement::getElement(state, classStackIndex);
222   if (element) {
223     element->setElt(unit, texgenFunc, texgenData);
224   }
225 }
226 
227 //!  FIXME: write doc.
228 
229 SoMultiTextureCoordinateElement::CoordType
getType(const int unit) const230 SoGLMultiTextureCoordinateElement::getType(const int unit) const
231 {
232   if (unit < PRIVATE(this)->unitdata.getLength()) {
233     if (PRIVATE(this)->unitdata[unit].texgenCB) return SoMultiTextureCoordinateElement::NONE;
234   }
235   return inherited::getType(unit);
236 }
237 
238 //!  FIXME: write doc.
239 
240 const SoGLMultiTextureCoordinateElement *
getInstance(SoState * const state)241 SoGLMultiTextureCoordinateElement::getInstance(SoState * const state)
242 {
243   return (SoGLMultiTextureCoordinateElement*)
244     SoElement::getConstElement(state, classStackIndex);
245 }
246 
247 //!  FIXME: write doc.
248 
249 void
send(const int unit,const int index) const250 SoGLMultiTextureCoordinateElement::send(const int unit, const int index) const
251 {
252   const UnitData & ud = this->getUnitData(unit);
253   GLenum glunit = (GLenum) (int(GL_TEXTURE0) + unit);
254   const cc_glglue * glue = PRIVATE(this)->glue;
255 
256   assert(unit < PRIVATE(this)->sendlookup.getLength());
257   switch (PRIVATE(this)->sendlookup[unit]) {
258   case SoGLMultiTextureCoordinateElementP::UNINITIALIZED:
259     assert(0 && "should not happen");
260     break;
261   case SoGLMultiTextureCoordinateElementP::NONE:
262     break;
263   case SoGLMultiTextureCoordinateElementP::FUNCTION:
264     assert(0 && "should not happen");
265     break;
266   case SoGLMultiTextureCoordinateElementP::TEXCOORD2:
267     assert(index < ud.numCoords);
268     cc_glglue_glMultiTexCoord2fv(glue, glunit, ud.coords2[index].getValue());
269     break;
270   case SoGLMultiTextureCoordinateElementP::TEXCOORD3:
271     cc_glglue_glMultiTexCoord3fv(glue, glunit, ud.coords3[index].getValue());
272     break;
273   case SoGLMultiTextureCoordinateElementP::TEXCOORD4:
274     cc_glglue_glMultiTexCoord4fv(glue, glunit, ud.coords4[index].getValue());
275     break;
276   default:
277     assert(0 && "should not happen");
278     break;
279   }
280 }
281 
282 //!  FIXME: write doc.
283 
284 void
send(const int unit,const int index,const SbVec3f & c,const SbVec3f & n) const285 SoGLMultiTextureCoordinateElement::send(const int unit,
286                                         const int index,
287                                         const SbVec3f &c,
288                                         const SbVec3f &n) const
289 {
290   const UnitData & ud = this->getUnitData(unit);
291   GLenum glunit = (GLenum) (int(GL_TEXTURE0) + unit);
292   const cc_glglue * glue = PRIVATE(this)->glue;
293 
294   assert(unit < PRIVATE(this)->sendlookup.getLength());
295   switch (PRIVATE(this)->sendlookup[unit]) {
296   case SoGLMultiTextureCoordinateElementP::NONE:
297     break;
298   case SoGLMultiTextureCoordinateElementP::FUNCTION:
299     assert(ud.funcCB);
300     cc_glglue_glMultiTexCoord4fv(glue, glunit,
301                                  ud.funcCB(ud.funcCBData, c, n).getValue());
302 
303     break;
304   case SoGLMultiTextureCoordinateElementP::TEXCOORD2:
305     cc_glglue_glMultiTexCoord2fv(glue, glunit, ud.coords2[index].getValue());
306     break;
307   case SoGLMultiTextureCoordinateElementP::TEXCOORD3:
308     cc_glglue_glMultiTexCoord3fv(glue, glunit, ud.coords3[index].getValue());
309     break;
310   case SoGLMultiTextureCoordinateElementP::TEXCOORD4:
311     cc_glglue_glMultiTexCoord4fv(glue, glunit, ud.coords4[index].getValue());
312     break;
313   default:
314     assert(0 && "should not happen");
315     break;
316   }
317 }
318 
319 //!  FIXME: write doc.
320 
321 void
setElt(const int unit,SoTexCoordTexgenCB * func,void * data)322 SoGLMultiTextureCoordinateElement::setElt(const int unit,
323                                           SoTexCoordTexgenCB * func,
324                                           void *data)
325 {
326   PRIVATE(this)->ensureCapacity(unit);
327   GLUnitData & ud = PRIVATE(this)->unitdata[unit];
328 
329   SbBool enablegen = FALSE;
330   SbBool disablegen = FALSE;
331   SbBool docallback = FALSE;
332 
333   if (func && !ud.texgenCB) {enablegen = TRUE; docallback = TRUE;}
334   else if (!func && ud.texgenCB) disablegen = TRUE;
335   else if (func /* && func != ud.texgenCB */) docallback = TRUE;
336 
337   /*
338   The last part of the above if else statement was modified because example 7.3 from The Inventor
339   Mentor was not being correctly reproduced. However, the above solution causes a reduction in
340   execution efficiency.  So...
341 
342   FIXME: Consider whether the caching mechanism can be used to overcome this problem.
343 
344   RHW 20141007
345   */
346 
347   if (func) {
348     // update SoMultiTextureCoordinateElement type
349     this->getUnitData(unit).whatKind = SoMultiTextureCoordinateElement::FUNCTION;
350   }
351   ud.texgenCB = func;
352   ud.texgenData = data;
353 
354   const cc_glglue * glue = cc_glglue_instance(PRIVATE(this)->contextid);
355 
356   if (enablegen || disablegen || docallback) {
357     cc_glglue_glActiveTexture(glue, (GLenum) (int(GL_TEXTURE0) + unit));
358   }
359 
360   if (enablegen) {
361     glEnable(GL_TEXTURE_GEN_S);
362     glEnable(GL_TEXTURE_GEN_T);
363     glEnable(GL_TEXTURE_GEN_R);
364     glEnable(GL_TEXTURE_GEN_Q);
365   }
366   if (disablegen) {
367     glDisable(GL_TEXTURE_GEN_S);
368     glDisable(GL_TEXTURE_GEN_T);
369     glDisable(GL_TEXTURE_GEN_R);
370     glDisable(GL_TEXTURE_GEN_Q);
371   }
372   if (docallback) this->doCallback(unit);
373 
374   if (enablegen || disablegen || docallback) {
375     cc_glglue_glActiveTexture(glue, (GLenum) GL_TEXTURE0);
376   }
377 }
378 
379 void
doCallback(const int unit) const380 SoGLMultiTextureCoordinateElement::doCallback(const int unit) const
381 {
382   if (PRIVATE(this)->unitdata[unit].texgenCB) {
383     PRIVATE(this)->unitdata[unit].texgenCB(PRIVATE(this)->unitdata[unit].texgenData);
384   }
385 }
386 
387 /*!
388   Internal method that is called from SoGLTextureCoordinateBundle to
389   set up optimized rendering.
390 */
391 void
initRender(const SbBool * enabled,const int maxenabled) const392 SoGLMultiTextureCoordinateElement::initRender(const SbBool * enabled, const int maxenabled) const
393 {
394   PRIVATE(this)->glue = cc_glglue_instance(PRIVATE(this)->contextid);
395   PRIVATE(this)->sendlookup.truncate(0);
396   for (int i = 0; i <= maxenabled; i++) {
397     PRIVATE(this)->sendlookup.append(SoGLMultiTextureCoordinateElementP::NONE);
398     // init the sendloopup variable
399     if (enabled[i]) {
400       const UnitData & ud = this->getUnitData(i);
401       switch (ud.whatKind) {
402       case SoMultiTextureCoordinateElement::DEFAULT:
403         assert(0 && "should not happen");
404         break;
405       case SoMultiTextureCoordinateElement::FUNCTION:
406         if (ud.funcCB) {
407           PRIVATE(this)->sendlookup[i] = SoGLMultiTextureCoordinateElementP::FUNCTION;
408         }
409         break;
410       case SoMultiTextureCoordinateElement::NONE:
411         break;
412       case SoMultiTextureCoordinateElement::EXPLICIT:
413         {
414           switch (ud.coordsDimension) {
415           case 2:
416             PRIVATE(this)->sendlookup[i] = SoGLMultiTextureCoordinateElementP::TEXCOORD2;
417             break;
418           case 3:
419             PRIVATE(this)->sendlookup[i] = SoGLMultiTextureCoordinateElementP::TEXCOORD3;
420             break;
421           case 4:
422             PRIVATE(this)->sendlookup[i] = SoGLMultiTextureCoordinateElementP::TEXCOORD4;
423             break;
424           default:
425             assert(0 && "should not happen");
426             break;
427           }
428         }
429         break;
430       default:
431         assert(0 && "should not happen");
432         break;
433       }
434     }
435   }
436 }
437 
438 /*!
439   Called from SoTextureCoordinateBundle to initialize multi texturing.
440 
441   \internal
442 */
443 void
initMulti(SoState * state) const444 SoGLMultiTextureCoordinateElement::initMulti(SoState * state) const
445 {
446   this->multienabled = SoMultiTextureEnabledElement::getEnabledUnits(state,
447                                                                      this->multimax);
448   this->initRender(this->multienabled, this->multimax);
449 }
450 
451 
452 #undef PRIVATE
453