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 SoTextureCoordinateBundle include/Inventor/bundles/SoTextureCoordinateBundle.h
35   \brief The SoTextureCoordinateBundle class simplifies texture coordinate handling.
36 
37   \ingroup bundles
38 
39   It is unlikely that application programmers should need to know how
40   to use this class, as it is mostly intended for internal use.
41 */
42 // FIXME: document class better.
43 
44 
45 #include <Inventor/bundles/SoTextureCoordinateBundle.h>
46 
47 #include <Inventor/misc/SoState.h>
48 #include <Inventor/elements/SoMultiTextureImageElement.h>
49 #include <Inventor/elements/SoGLMultiTextureEnabledElement.h>
50 #include <Inventor/elements/SoGLMultiTextureCoordinateElement.h>
51 #include <Inventor/elements/SoBumpMapElement.h>
52 #include <Inventor/elements/SoBumpMapCoordinateElement.h>
53 
54 #include <Inventor/nodes/SoShape.h>
55 #include <Inventor/nodes/SoVertexProperty.h>
56 #include <Inventor/actions/SoPickAction.h>
57 #include <Inventor/actions/SoGLRenderAction.h>
58 #include <Inventor/caches/SoBoundingBoxCache.h>
59 
60 #ifdef HAVE_CONFIG_H
61 #include <config.h>
62 #endif
63 
64 #include <Inventor/system/gl.h>
65 
66 #include <cassert>
67 #include "SbBasicP.h"
68 #include "coindefs.h"
69 
70 #define FLAG_FUNCTION           0x01
71 #define FLAG_NEEDCOORDS         0x02
72 #define FLAG_DEFAULT            0x04
73 #define FLAG_DIDPUSH            0x08
74 #define FLAG_3DTEXTURES         0x10
75 #define FLAG_DIDINITDEFAULT     0x20
76 #define FLAG_NEEDINDICES        0x40
77 
78 /*!
79   Constructor with \a action being the action applied to the node.
80   The \a forRendering parameter must be \e TRUE if the bundle is to
81   be used for sending texture coordinates to GL during rendering.
82   The \a setUpDefault must be \e TRUE if default texture coordinates
83   should be generated.
84 */
85 SoTextureCoordinateBundle::
SoTextureCoordinateBundle(SoAction * const action,const SbBool forRendering,const SbBool setUpDefault)86 SoTextureCoordinateBundle(SoAction * const action,
87                           const SbBool forRendering,
88                           const SbBool setUpDefault)
89   : SoBundle(action)
90 {
91   this->flags = 0;
92   //
93   // return immediately if there is no texture
94   //
95   int lastenabled = -1;
96   const SbBool * multienabled =
97     SoMultiTextureEnabledElement::getEnabledUnits(this->state, lastenabled);
98   SbBool needinit = lastenabled >= 0;
99   SbBool glrender = forRendering || action->isOfType(SoGLRenderAction::getClassTypeId());
100   SbBool bumpenabled = glrender && (SoBumpMapElement::get(this->state) != NULL);
101 
102   if (!needinit && !multienabled && !bumpenabled) return;
103 
104   // It is safe to assume that shapenode is of type SoShape, so we
105   // cast to SoShape before doing any operations on the node.
106   this->shapenode = coin_assert_cast<SoShape *>(action->getCurPathTail());
107 
108   this->coordElt = SoMultiTextureCoordinateElement::getInstance(this->state);
109   for (int i = 0; i <= lastenabled; i++) {
110     if (multienabled[i]) {
111       switch (this->coordElt->getType(i)) {
112       case SoMultiTextureCoordinateElement::DEFAULT:
113         this->initDefault(action, i);
114         break;
115       case SoMultiTextureCoordinateElement::EXPLICIT:
116         this->flags |= FLAG_NEEDINDICES;
117         if (this->coordElt->getNum(i) > 0) {
118           this->flags |= FLAG_NEEDCOORDS;
119         }
120         else {
121           this->initDefault(action, i);
122         }
123         break;
124       case SoMultiTextureCoordinateElement::FUNCTION:
125         this->flags |= FLAG_FUNCTION;
126         this->flags |= FLAG_NEEDCOORDS; // not automatically generated
127         break;
128 
129       case SoMultiTextureCoordinateElement::TEXGEN:
130         // texcoord won't be needed. This will only happen during SoGLRenderAction,
131         // when GL generates texture coorinates. Therefore, we will not set
132         // the FLAG_NEEDCOORDS here.
133         this->flags |= FLAG_FUNCTION;
134         if (!forRendering) {
135           this->flags |= FLAG_NEEDCOORDS;
136         }
137         break;
138       default:
139         assert(0 && "unknown CoordType");
140         break;
141       }
142     }
143   }
144   // refetch element in case we pushed
145   if (this->flags & FLAG_DIDPUSH) {
146     this->coordElt = SoMultiTextureCoordinateElement::getInstance(this->state);
147   }
148   this->glElt = NULL;
149   if (glrender) {
150     SbBool needindices = FALSE;
151     if (!needindices && this->isFunction()) {
152       // check if bump mapping needs texture coordinate indices
153       if (bumpenabled &&
154           (SoBumpMapCoordinateElement::getInstance(state)->getNum())) {
155         needindices = TRUE;
156       }
157     }
158     if (needindices && this->isFunction()) this->flags |= FLAG_NEEDINDICES;
159     this->glElt = static_cast<const SoGLMultiTextureCoordinateElement *>(this->coordElt);
160     this->glElt->initMulti(action->getState());
161   }
162   if ((this->flags & FLAG_DEFAULT) && !setUpDefault) {
163     // FIXME: I couldn't be bothered to support this yet. It is for picking
164     // optimization only, I think. pederb, 20000218
165   }
166 }
167 
168 /*!
169   Destructor.
170 */
~SoTextureCoordinateBundle()171 SoTextureCoordinateBundle::~SoTextureCoordinateBundle()
172 {
173   if (this->flags & FLAG_DIDPUSH) this->state->pop();
174 }
175 
176 /*!
177   Returns \e TRUE if texture coordinates is needed during rendering.
178 */
179 SbBool
needCoordinates() const180 SoTextureCoordinateBundle::needCoordinates() const
181 {
182   return (this->flags & FLAG_NEEDCOORDS) != 0;
183 }
184 
185 /*!
186   Returns \e TRUE if a texture coordinate function should be used.
187 */
188 SbBool
isFunction() const189 SoTextureCoordinateBundle::isFunction() const
190 {
191   return (this->flags & FLAG_FUNCTION) != 0;
192 }
193 
194 /*!
195 
196   Returns \e TRUE if isFunction() is \e TRUE, but the texture
197   coordinate indices are needed either by bump mapping or by one of
198   the other texture units.
199 
200   \since Coin 2.2
201 */
202 SbBool
needIndices(void) const203 SoTextureCoordinateBundle::needIndices(void) const
204 {
205   return (this->flags & FLAG_NEEDINDICES) != 0;
206 }
207 
208 
209 /*!
210   Returns the texture coordinates based on \a point and \a normal.
211   Should only be used if SoTextureCoordinateBundle::isFunction() is \a TRUE.
212 */
213 const SbVec4f &
get(const SbVec3f & point,const SbVec3f & normal)214 SoTextureCoordinateBundle::get(const SbVec3f &point, const SbVec3f &normal)
215 {
216   assert(this->coordElt != NULL && (this->flags & FLAG_FUNCTION));
217   if (this->flags & FLAG_DEFAULT) {
218     SbVec3f pt;
219     if (this->flags & FLAG_3DTEXTURES) {
220       pt = point - this->defaultorigo;
221       this->dummyInstance[2] = pt[2]/this->defaultsize[2];
222     }
223     else {
224       pt.setValue(point[this->defaultdim0]-this->defaultorigo[0],
225                   point[this->defaultdim1]-this->defaultorigo[1],
226                   0.0f);
227     }
228     this->dummyInstance[0] = pt[0]/this->defaultsize[0];
229     this->dummyInstance[1] = pt[1]/this->defaultsize[1];
230     return this->dummyInstance;
231   }
232   else {
233     return coordElt->get(point, normal);
234   }
235 }
236 
237 /*!
238   Returns the texture coordinates at index \a index.
239   Should only be used if SoTextureCoordinateBundle::isFunction() is \a FALSE.
240 */
241 const SbVec4f &
get(const int index)242 SoTextureCoordinateBundle::get(const int index)
243 {
244   assert(coordElt && !(this->flags & FLAG_FUNCTION));
245   return coordElt->get4(index);
246 }
247 
248 
249 /*!
250   \fn void SoTextureCoordinateBundle::send(const int index) const
251   Send texture coordinates to GL. Should only be used if
252   SoTextureCoordinateBundle::isFunction() is \a FALSE.
253 */
254 
255 /*!
256   \fn void SoTextureCoordinateBundle::send(const int index, const SbVec3f &point, const SbVec3f &normal) const
257   Convenience function that will make it transparent to the rendering
258   code if ordinary texture coordinates or function texture coordinates
259   are used.
260 */
261 
262 
263 //
264 // initialize default texture coordinates for unit
265 //
266 void
initDefault(SoAction * action,const int unit)267 SoTextureCoordinateBundle::initDefault(SoAction * action, const int unit)
268 {
269   this->flags |= FLAG_NEEDCOORDS;
270   this->flags |= FLAG_DEFAULT;
271   this->flags |= FLAG_FUNCTION;
272 
273   if (!(this->flags & FLAG_DIDPUSH)) {
274     this->state->push();
275     this->flags |= FLAG_DIDPUSH;
276   }
277   SoMultiTextureCoordinateElement::setFunction(this->state, this->shapenode, unit,
278                                                SoTextureCoordinateBundle::defaultCBMulti,
279                                                this);
280   if (!(this->flags & FLAG_DIDINITDEFAULT)) {
281     this->initDefaultCallback(action);
282   }
283 }
284 
285 //
286 // callback for default texture coordinates (for texture unit 0)
287 //
288 const SbVec4f &
defaultCB(void * userdata,const SbVec3f & point,const SbVec3f & normal)289 SoTextureCoordinateBundle::defaultCB(void * userdata,
290                                      const SbVec3f & point,
291                                      const SbVec3f & normal)
292 {
293   return static_cast<SoTextureCoordinateBundle *>(userdata)->get(point, normal);
294 }
295 
296 //
297 // callback for default texture coordinates (for texture units > 0)
298 //
299 const SbVec4f &
defaultCBMulti(void * userdata,const SbVec3f & point,const SbVec3f & COIN_UNUSED_ARG (normal))300 SoTextureCoordinateBundle:: defaultCBMulti(void * userdata,
301                                            const SbVec3f & point,
302                                            const SbVec3f & COIN_UNUSED_ARG(normal))
303 {
304   SoTextureCoordinateBundle * thisp = static_cast<SoTextureCoordinateBundle *>(userdata);
305 
306   SbVec3f pt;
307   if (thisp->flags & FLAG_3DTEXTURES) {
308     pt = point - thisp->defaultorigo;
309     thisp->dummyInstance[2] = pt[2]/thisp->defaultsize[2];
310   }
311   else {
312     pt.setValue(point[thisp->defaultdim0]-thisp->defaultorigo[0],
313                 point[thisp->defaultdim1]-thisp->defaultorigo[1],
314                 0.0f);
315   }
316   thisp->dummyInstance[0] = pt[0]/thisp->defaultsize[0];
317   thisp->dummyInstance[1] = pt[1]/thisp->defaultsize[1];
318   return thisp->dummyInstance;
319 }
320 
321 //
322 // Set up stuff needed for default texture coordinate mapping callback
323 //
324 void
initDefaultCallback(SoAction * action)325 SoTextureCoordinateBundle::initDefaultCallback(SoAction * action)
326 {
327   this->flags |= FLAG_DIDINITDEFAULT;
328   //
329   // calculate needed stuff for default mapping
330   //
331   SbBox3f box;
332   SbVec3f center;
333   // this could be very slow, but if you're looking for speed, default
334   // texture coordinate mapping shouldn't be used. We might optimize this
335   // by using a SoTextureCoordinateCache soon though. pederb, 20000218
336 
337   SoShape * shape = coin_assert_cast<SoShape *>(this->shapenode);
338   const SoBoundingBoxCache * bboxcache = shape->getBoundingBoxCache();
339   if (bboxcache && bboxcache->isValid(action->getState())) {
340     box = bboxcache->getProjectedBox();
341     if (bboxcache->isCenterSet()) center = bboxcache->getCenter();
342     else center = box.getCenter();
343   }
344   else {
345     shape->computeBBox(action, box, center);
346   }
347 
348   // just use som default values if the shape bbox is empty
349   SbVec3f size(1.0f, 1.0f, 1.0f);
350   SbVec3f origo(0.f, 0.0f, 0.0f);
351   if (!box.isEmpty()) {
352     box.getSize(size[0], size[1], size[2]);
353     origo = box.getMin();
354   }
355 
356   // Map S,T,R to X,Y,Z for 3D texturing
357   if (SoMultiTextureEnabledElement::getMode(this->state, 0) ==
358       SoMultiTextureEnabledElement::TEXTURE3D) {
359     this->flags |= FLAG_3DTEXTURES;
360     this->defaultdim0 = 0;
361     this->defaultdim1 = 1;
362 
363     this->defaultorigo[2] = origo[2];
364     this->defaultsize[2] = size[2];
365   }
366   else { // 2D textures
367     this->defaultsize[2] = 1.0f;
368     this->flags &= ~FLAG_3DTEXTURES;
369     // find the two biggest dimensions
370     int smallest = 0;
371     float smallval = size[0];
372     if (size[1] < smallval) {
373       smallest = 1;
374       smallval = size[1];
375     }
376     if (size[2] < smallval) {
377       smallest = 2;
378     }
379 
380     this->defaultdim0 = (smallest + 1) % 3;
381     this->defaultdim1 = (smallest + 2) % 3;
382 
383     if (size[this->defaultdim0] == size[this->defaultdim1]) {
384       // FIXME: this is probably an OIV bug. The OIV man pages are not
385       // clear on this point (surprise), but the VRML specification states
386       // that if the two dimensions are equal, the ordering X>Y>Z should
387       // be used.
388 #if 0 // the correct way to do it
389       if (this->defaultdim0 > this->defaultdim1) {
390         SbSwap(this->defaultdim0, this->defaultdim1);
391       }
392 #else // the OIV way to do it.
393       if (this->defaultdim0 < this->defaultdim1) {
394         SbSwap(this->defaultdim0, this->defaultdim1);
395       }
396 #endif // OIV compatibility fix
397     }
398     else if (size[this->defaultdim0] < size[this->defaultdim1]) {
399       SbSwap(this->defaultdim0, this->defaultdim1);
400     }
401   }
402 
403   this->defaultorigo[0] = origo[this->defaultdim0];
404   this->defaultorigo[1] = origo[this->defaultdim1];
405   this->defaultsize[0] = size[this->defaultdim0];
406   this->defaultsize[1] = size[this->defaultdim1];
407 
408   // if bbox is empty in one dimension we just want to set it to
409   // 0.0. The size should be set to 1.0 to avoid division by zero.
410   for (int i = 0; i < 3; i++) {
411     if (this->defaultsize[i] <= 0.0f) {
412       this->defaultsize[i] = 1.0f;
413     }
414   }
415 
416   this->dummyInstance[2] = 0.0f;
417   this->dummyInstance[3] = 1.0f;
418 
419   assert(this->defaultsize[0] > 0.0f);
420   assert(this->defaultsize[1] > 0.0f);
421 }
422 
423 #undef FLAG_FUNCTION
424 #undef FLAG_NEEDCOORDS
425 #undef FLAG_DEFAULT
426 #undef FLAG_DIDPUSH
427 #undef FLAG_3DTEXTURES
428 #undef FLAG_DIDINITDEFAULT
429 #undef FLAG_NEEDINDICES
430