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