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