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 #include "shapenodes/soshape_bigtexture.h"
34 #include "coindefs.h"
35
36 #include <cstdlib>
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif // HAVE_CONFIG_H
41
42 #include <Inventor/SbClip.h>
43 #include <Inventor/SbPlane.h>
44 #include <Inventor/SoPrimitiveVertex.h>
45 #include <Inventor/SbVec2f.h>
46 #include <Inventor/misc/SoGLBigImage.h>
47 #include <Inventor/SbBox3f.h>
48 #include <Inventor/SbBox2f.h>
49 #include <Inventor/elements/SoCullElement.h>
50 #include <Inventor/elements/SoMultiTextureMatrixElement.h>
51 #include <Inventor/elements/SoMultiTextureImageElement.h>
52 #include <Inventor/bundles/SoMaterialBundle.h>
53 #include <Inventor/nodes/SoShape.h>
54 #include <Inventor/C/tidbits.h>
55 #include <Inventor/system/gl.h>
56
soshape_bigtexture(void)57 soshape_bigtexture::soshape_bigtexture(void)
58 {
59 this->clipper = NULL;
60 this->pvlist = NULL;
61 this->regions = NULL;
62 this->numallocregions = 0;
63 }
64
~soshape_bigtexture()65 soshape_bigtexture::~soshape_bigtexture()
66 {
67 delete[] this->regions;
68 delete this->clipper;
69
70 if (this->pvlist) {
71 int n = this->pvlist->getLength();
72 for (int i = 0; i < n; i++) {
73 delete (*this->pvlist)[i];
74 }
75 delete this->pvlist;
76 }
77 }
78
79 void
beginShape(SoGLBigImage * imageptr,const float qualityarg)80 soshape_bigtexture::beginShape(SoGLBigImage * imageptr,
81 const float qualityarg)
82 {
83 this->image = imageptr;
84 this->quality = qualityarg;
85 this->pvlistcnt = 0;
86 this->vertexlist.truncate(0);
87
88 // FIXME: hardcoding for 265x256 tiles is a bad strategy, as it will
89 // often give bad performance vs larger tile sizes. See the
90 // elaborate FIXME note on this issue in SoGLBigImage.cpp
91 // initSubImages(). 20050701 mortene.
92 int size = 256;
93 int num = imageptr->initSubImages(SbVec2s(size, size));
94
95 // try to not use more than 256 subtextures, but don't use a
96 // subimage size bigger than 1024 (it will be too slow to
97 // recalculate the subimage for larger images)
98 while (num > 256 && size < 1024) {
99 size <<= 1;
100 num = imageptr->initSubImages(SbVec2s(size, size));
101 }
102 this->numregions = num;
103
104 if (this->clipper == NULL) {
105 this->clipper = new SbClip(clipcb, this);
106 this->pvlist = new SbList <SoPrimitiveVertex*>;
107 this->regions = new bt_region[num];
108 this->numallocregions = num;
109 }
110 if (num > this->numallocregions) {
111 delete[] this->regions;
112 this->regions = new bt_region[num];
113 this->numallocregions = num;
114 }
115 for (int i = 0; i < num; i++) {
116 bt_region & reg = this->regions[i];
117 reg.facelist.truncate(0);
118 reg.pvlist.truncate(0);
119 this->image->handleSubImage(i,
120 reg.start,
121 reg.end,
122 reg.tcmul);
123
124 // The boundary planes are problematic because the endpositions
125 // calculated by handleSubImage are greater than 1 in certain
126 // circumstances. This is because SoGLBigImage divides the image
127 // into equally sized tiles. Some of the bordertiles might have
128 // endpoints outside of the original image. This causes the
129 // clipping planes to be positioned wrongly, so we have to check
130 // the bordercases, that is clamp the distance to be <= 1. This is
131 // only the case for the endpoints, not the startpoints which
132 // always are within the original texture.
133 reg.planes[0] =
134 SbPlane(SbVec3f(1.0f, 0.0f, 0.0f), reg.start[0]);
135 reg.planes[1] =
136 SbPlane(SbVec3f(0.0f, 1.0f, 0.0f), reg.start[1]);
137 reg.planes[2] =
138 SbPlane(SbVec3f(-1.0f, 0.0f, 0.0f), -SbMin(reg.end[0], 1.0f));
139 reg.planes[3] =
140 SbPlane(SbVec3f(0.0f, -1.0f, 0.0f), -SbMin(reg.end[1], 1.0f));
141 }
142 }
143
144 SbBool
endShape(SoState * state,SoShape * shape,SoMaterialBundle & mb)145 soshape_bigtexture::endShape(SoState * state,
146 SoShape * shape,
147 SoMaterialBundle & mb)
148 {
149 this->clip_triangles(state);
150
151 // clear texture matrix. We've already calculated the world space
152 // texture coordinates.
153 glMatrixMode(GL_TEXTURE);
154 glPushMatrix();
155 glLoadIdentity();
156 glMatrixMode(GL_MODELVIEW);
157
158 // disable texgen functions, we always supply texture coordinates
159 glPushAttrib(GL_ENABLE_BIT);
160 glDisable(GL_TEXTURE_GEN_S);
161 glDisable(GL_TEXTURE_GEN_T);
162 glDisable(GL_TEXTURE_GEN_R);
163 glDisable(GL_TEXTURE_GEN_Q);
164
165 const int numreg = this->numregions;
166 for (int i = 0; i < numreg; i++) {
167 int numv, j;
168
169 const bt_region & reg = this->regions[i];
170 int numface = reg.facelist.getLength();
171 if (numface == 0) continue;
172
173 numv = reg.pvlist.getLength();
174 SbBox3f bbox;
175 for (j = 0; j < numv; j++) {
176 bbox.extendBy(reg.pvlist[j]->getPoint());
177 }
178 SbVec2s rectsize;
179 shape->getScreenSize(state, bbox, rectsize);
180 this->image->applySubImage(state, i, this->quality, rectsize);
181 int vcnt = 0;
182 for (j = 0; j < numface; j++) {
183 glBegin(GL_TRIANGLE_FAN);
184 numv = reg.facelist[j];
185 for (int k = 0; k < numv; k++) {
186 SoPrimitiveVertex * v = reg.pvlist[vcnt++];
187 SbVec4f tc = v->getTextureCoords();
188 tc[0] -= reg.start[0];
189 tc[1] -= reg.start[1];
190 tc[0] /= (reg.end[0]-reg.start[0]);
191 tc[1] /= (reg.end[1]-reg.start[1]);
192 glTexCoord4fv(tc.getValue());
193 glNormal3fv(v->getNormal().getValue());
194 mb.send(v->getMaterialIndex(), TRUE);
195 glVertex3fv(v->getPoint().getValue());
196 }
197 glEnd();
198 }
199 }
200
201 // enable texgen (if active)
202 glPopAttrib();
203
204 // restore texture matrix
205 glMatrixMode(GL_TEXTURE);
206 glPopMatrix();
207 glMatrixMode(GL_MODELVIEW);
208
209 // return TRUE if all textures were created in the correct resolution
210 return ! this->image->exceededChangeLimit();
211 }
212
213 void
triangle(SoState * COIN_UNUSED_ARG (state),const SoPrimitiveVertex * v1,const SoPrimitiveVertex * v2,const SoPrimitiveVertex * v3)214 soshape_bigtexture::triangle(SoState * COIN_UNUSED_ARG(state),
215 const SoPrimitiveVertex * v1,
216 const SoPrimitiveVertex * v2,
217 const SoPrimitiveVertex * v3)
218 {
219 const SoPrimitiveVertex * vp[] = {v1, v2, v3};
220
221 for (int i = 0; i < 3; i++) {
222 SoPrimitiveVertex * pv = this->get_new_pv();
223 *pv = *(vp[i]);
224 this->vertexlist.append(pv);
225 }
226 }
227
228 void
clip_triangles(SoState * state)229 soshape_bigtexture::clip_triangles(SoState * state)
230 {
231 int n = this->vertexlist.getLength();
232 if (n == 0) return;
233
234 // need texture matrix to transform the texture coordinates
235 SbMatrix texturematrix = SoMultiTextureMatrixElement::get(state, 0);
236 SoMultiTextureImageElement::Wrap wrap[2];
237 SbVec2s dummy;
238 int dummync;
239 SbColor dummycol;
240 SoMultiTextureImageElement::Model dummymod;
241
242 // need wrapS/T to figure out how to handle the texture coordinates
243 (void) SoMultiTextureImageElement::get(state, 0, dummy, dummync,
244 wrap[0], wrap[1],
245 dummymod, dummycol);
246 SbVec4f tmp;
247 int i;
248
249 for (i = 0; i < n; i++) {
250 tmp = this->vertexlist[i]->getTextureCoords();
251 texturematrix.multVecMatrix(tmp, tmp);
252 SbVec3f tmp3;
253 tmp.getReal(tmp3);
254 this->vertexlist[i]->setTextureCoords(tmp3);
255 }
256
257 // Clip the triangles agains the clippingplanes of the windows it
258 // passes through. Windows can be seen as the integer components of
259 // the texturecoordinate. The number of windows a triangle passes
260 // through is indicated by the bounding box of the triangle's
261 // texturecoordinates.
262 for (i = 0; i < n; i += 3) {
263 // Take care of the texturecoords for easy processing
264 SbVec4f tc[3], tcf[3];
265 tc[0] = this->vertexlist[i]->getTextureCoords();
266 tc[1] = this->vertexlist[i+1]->getTextureCoords();
267 tc[2] = this->vertexlist[i+2]->getTextureCoords();
268
269 // Calculate triangle's texturecoordinate bounding box
270 SbBox2f bbox;
271 bbox.extendBy(SbVec2f(tc[0][0], tc[0][1]));
272 bbox.extendBy(SbVec2f(tc[1][0], tc[1][1]));
273 bbox.extendBy(SbVec2f(tc[2][0], tc[2][1]));
274
275 // Find the min/max bounds of the bounding box
276 SbVec2f bbmin = bbox.getMin();
277 SbVec2f bbmax = bbox.getMax();
278
279 // Find the intersecting windows
280 const int windowstartidxx = (int)floor(bbmin[0]);
281 const int windowstartidxy = (int)floor(bbmin[1]);
282 const int windowendidxx = (int)ceil(bbmax[0]);
283 const int windowendidxy = (int)ceil(bbmax[1]);
284
285 // Do the actual clipping against the windows
286 for (int wy = windowstartidxy; wy < windowendidxy; wy++) {
287 for (int wx = windowstartidxx; wx < windowendidxx; wx++) {
288 // Position the texturecoordinates according to the
289 // current window.
290 const int transs = -wx;
291 const int transt = -wy;
292
293 tcf[0] = tc[0];
294 tcf[1] = tc[1];
295 tcf[2] = tc[2];
296
297 tcf[0][0] = tcf[0][0] + transs;
298 tcf[0][1] = tcf[0][1] + transt;
299
300 tcf[1][0] = tcf[1][0] + transs;
301 tcf[1][1] = tcf[1][1] + transt;
302
303 tcf[2][0] = tcf[2][0] + transs;
304 tcf[2][1] = tcf[2][1] + transt;
305
306 this->vertexlist[i]->setTextureCoords(tcf[0]);
307 this->vertexlist[i+1]->setTextureCoords(tcf[1]);
308 this->vertexlist[i+2]->setTextureCoords(tcf[2]);
309
310 // Clip the triangle against the current window
311 this->handle_triangle(state,
312 this->vertexlist[i],
313 this->vertexlist[i+1],
314 this->vertexlist[i+2],
315 wrap, transs, transt);
316 }
317 }
318 }
319 }
320
321 void
handle_triangle(SoState * state,SoPrimitiveVertex * v1,SoPrimitiveVertex * v2,SoPrimitiveVertex * v3,const SoMultiTextureImageElement::Wrap wrap[2],const int transs,const int transt)322 soshape_bigtexture::handle_triangle(SoState * state,
323 SoPrimitiveVertex * v1,
324 SoPrimitiveVertex * v2,
325 SoPrimitiveVertex * v3,
326 const SoMultiTextureImageElement::Wrap wrap[2],
327 const int transs,
328 const int transt)
329 {
330 SbVec4f tc[3];
331 tc[0] = v1->getTextureCoords();
332 tc[1] = v2->getTextureCoords();
333 tc[2] = v3->getTextureCoords();
334
335 SbBox2f bbox;
336 bbox.extendBy(SbVec2f(tc[0][0], tc[0][1]));
337 bbox.extendBy(SbVec2f(tc[1][0], tc[1][1]));
338 bbox.extendBy(SbVec2f(tc[2][0], tc[2][1]));
339 SbBox2f regbbox;
340
341 for (int i = 0; i < this->numregions; i++) {
342 bt_region * reg = &this->regions[i];
343 regbbox.makeEmpty();
344 regbbox.extendBy(reg->start);
345 regbbox.extendBy(reg->end);
346
347 // check if there is a chance for an intersection
348 if (regbbox.intersect(bbox)) {
349 this->clipper->reset();
350
351 // need copies
352 SoPrimitiveVertex * pv1 = this->get_new_pv();
353 *pv1 = *v1;
354 SoPrimitiveVertex * pv2 = this->get_new_pv();
355 *pv2 = *v2;
356 SoPrimitiveVertex * pv3 = this->get_new_pv();
357 *pv3 = *v3;
358
359 this->clipper->addVertex(SbVec3f(tc[0][0], tc[0][1], 0.0f), pv1);
360 this->clipper->addVertex(SbVec3f(tc[1][0], tc[1][1], 0.0f), pv2);
361 this->clipper->addVertex(SbVec3f(tc[2][0], tc[2][1], 0.0f), pv3);
362
363 this->clipper->clip(reg->planes[0]);
364 this->clipper->clip(reg->planes[1]);
365 this->clipper->clip(reg->planes[2]);
366 this->clipper->clip(reg->planes[3]);
367
368 const int numv = this->clipper->getNumVertices();
369 if (numv >= 3) {
370 int j, k;
371 SbBox3f obox;
372 for (j = 0; j < numv; j++) {
373 SoPrimitiveVertex * v = (SoPrimitiveVertex*) this->clipper->getVertexData(j);
374 obox.extendBy(v->getPoint());
375 }
376
377 if (!SoCullElement::cullTest(state, obox)) {
378
379 if (wrap[0] == SoMultiTextureImageElement::CLAMP ||
380 wrap[1] == SoMultiTextureImageElement::CLAMP) {
381
382 // Clamp the texturecoordinates
383 for (j = 0; j < numv; j++) {
384 SoPrimitiveVertex * v = (SoPrimitiveVertex *) this->clipper->getVertexData(j);
385 SbVec4f texcoord = v->getTextureCoords();
386
387 // Clamp the texturecoordinates to be within the
388 // clamp-region. Need to translate back to the texture
389 // coordinates original position to do this. That is
390 // because we are interested in the cases where the
391 // texture coordinates go outside the default clipping
392 // window.
393 if (wrap[0] == SoMultiTextureImageElement::CLAMP) {
394 texcoord[0] = SbClamp(texcoord[0] - transs, 0.0f, 1.0f);
395 }
396 if (wrap[1] == SoMultiTextureImageElement::CLAMP) {
397 texcoord[1] = SbClamp(texcoord[1] - transt, 0.0f, 1.0f);
398 }
399 v->setTextureCoords(texcoord);
400 }
401
402 // Find the region the polygon belongs to. This has to be
403 // done because the clamping alters the region. The mean
404 // texturecoordinates of a polygon are used to find the
405 // region the polygon belongs to.
406 for (k = 0; k < this->numregions; k++) {
407 reg = &this->regions[k];
408
409 // Find the mean texture coordinate for the
410 // polygon. This will be used to find the texture
411 // region.
412 SbVec2f mean(0.0f, 0.0f);
413 for (j = 0; j < numv; j++) {
414 SoPrimitiveVertex * v = (SoPrimitiveVertex *) this->clipper->getVertexData(j);
415 SbVec4f texcoord = v->getTextureCoords();
416
417 mean[0] += texcoord[0];
418 mean[1] += texcoord[1];
419 }
420 mean[0] /= (float)numv;
421 mean[1] /= (float)numv;
422
423 // Find the bounding box of the current region.
424 regbbox.makeEmpty();
425 regbbox.extendBy(reg->start);
426 regbbox.extendBy(reg->end);
427
428 // Test if the mean is inside the current region. If it
429 // is, all the texture coordinates for the polygon
430 // should be within the same region.
431 if (regbbox.intersect(mean)) {
432 break;
433 }
434 }
435 }
436
437 // Add the polygon to the region
438 reg->facelist.append(numv);
439 for (j = 0; j < numv; j++) {
440 reg->pvlist.append((SoPrimitiveVertex *) this->clipper->getVertexData(j));
441 }
442 }
443 }
444 }
445 }
446 }
447
448
449 SoPrimitiveVertex *
get_new_pv(void)450 soshape_bigtexture::get_new_pv(void)
451 {
452 if (this->pvlistcnt < this->pvlist->getLength())
453 return (*this->pvlist)[this->pvlistcnt++];
454 else {
455 SoPrimitiveVertex * pv = new SoPrimitiveVertex;
456 this->pvlistcnt++;
457 this->pvlist->append(pv);
458 return pv;
459 }
460 }
461
462 void *
clipcb(const SbVec3f & v0,void * vdata0,const SbVec3f & v1,void * vdata1,const SbVec3f & newvertex,void * userdata)463 soshape_bigtexture::clipcb(const SbVec3f & v0, void * vdata0,
464 const SbVec3f & v1, void * vdata1,
465 const SbVec3f & newvertex,
466 void * userdata)
467 {
468 soshape_bigtexture * thisp = (soshape_bigtexture*) userdata;
469
470 SoPrimitiveVertex * pv0 = (SoPrimitiveVertex*) vdata0;
471 SoPrimitiveVertex * pv1 = (SoPrimitiveVertex*) vdata1;
472
473 float dist = (v1-v0).length();
474 float newdist = (newvertex-v0).length();
475 if (dist == 0.0f) newdist = 0.0f;
476 else newdist /= dist;
477
478 SoPrimitiveVertex * pv = thisp->get_new_pv();
479 pv->setPoint(pv0->getPoint() + (pv1->getPoint()-pv0->getPoint()) * newdist);
480 pv->setTextureCoords(SbVec2f(newvertex[0], newvertex[1]));
481 pv->setNormal(pv0->getNormal() + (pv1->getNormal()-pv0->getNormal()) * newdist);
482 pv->setMaterialIndex(pv0->getMaterialIndex());
483 return pv;
484 }
485