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