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_trianglesort.h"
34 
35 #include <cstdlib>
36 #include <cassert>
37 
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif // HAVE_CONFIG_H
41 
42 #include <Inventor/lists/SbList.h>
43 #include <Inventor/SoPrimitiveVertex.h>
44 #include <Inventor/elements/SoShapeHintsElement.h>
45 #include <Inventor/elements/SoModelMatrixElement.h>
46 #include <Inventor/elements/SoProjectionMatrixElement.h>
47 #include <Inventor/elements/SoViewingMatrixElement.h>
48 #include <Inventor/elements/SoViewVolumeElement.h>
49 #include <Inventor/bundles/SoMaterialBundle.h>
50 #include <Inventor/SbPlane.h>
51 #include <Inventor/C/tidbits.h>
52 #include <Inventor/system/gl.h>
53 
soshape_trianglesort(void)54 soshape_trianglesort::soshape_trianglesort(void)
55 {
56   this->pvlist = NULL;
57   this->trianglelist = NULL;
58 }
59 
~soshape_trianglesort()60 soshape_trianglesort::~soshape_trianglesort()
61 {
62   delete this->pvlist;
63   delete this->trianglelist;
64 }
65 
66 void
beginShape(SoState *)67 soshape_trianglesort::beginShape(SoState *)
68 {
69   if (this->pvlist == NULL) {
70     this->pvlist = new SbList <SoPrimitiveVertex>;
71     this->trianglelist = new SbList <sorted_triangle>;
72   }
73   pvlist->truncate(0);
74 }
75 
76 void
triangle(SoState *,const SoPrimitiveVertex * v1,const SoPrimitiveVertex * v2,const SoPrimitiveVertex * v3)77 soshape_trianglesort::triangle(SoState *,
78                           const SoPrimitiveVertex * v1,
79                           const SoPrimitiveVertex * v2,
80                           const SoPrimitiveVertex * v3)
81 {
82   assert(this->pvlist);
83   this->pvlist->append(*v1);
84   this->pvlist->append(*v2);
85   this->pvlist->append(*v3);
86 }
87 
88 // qsort() callback.
89 //
90 // "extern C" wrapper is needed with the OSF1/cxx compiler (probably a
91 // bug in the compiler, but it doesn't seem to hurt to do this
92 // anyway).
93 extern "C" {
94 static int
compare_triangles(const void * ptr1,const void * ptr2)95 compare_triangles(const void * ptr1, const void * ptr2)
96 {
97   soshape_trianglesort::sorted_triangle * tri1 = (soshape_trianglesort::sorted_triangle*) ptr1;
98   soshape_trianglesort::sorted_triangle * tri2 = (soshape_trianglesort::sorted_triangle*) ptr2;
99 
100   if (tri1->dist > tri2->dist) return -1;
101   if (tri1->dist == tri2->dist) return tri2->backface - tri1->backface;
102   return 1;
103 }
104 }
105 
106 void
endShape(SoState * state,SoMaterialBundle & mb)107 soshape_trianglesort::endShape(SoState * state, SoMaterialBundle & mb)
108 {
109   int i, n = this->pvlist->getLength() / 3;
110   if (n == 0) return;
111 
112   const SoPrimitiveVertex * varray = this->pvlist->getArrayPtr();
113 
114   this->trianglelist->truncate(0);
115   sorted_triangle tri;
116 
117   const SoPrimitiveVertex * v;
118   const SbMatrix & mm = SoModelMatrixElement::get(state);
119 
120   SoShapeHintsElement::VertexOrdering vo;
121   SoShapeHintsElement::ShapeType st;
122   SoShapeHintsElement::FaceType ft;
123   SoShapeHintsElement::get(state, vo, st, ft);
124 
125   SbBool bfcull =
126     (vo != SoShapeHintsElement::UNKNOWN_ORDERING) &&
127     (st == SoShapeHintsElement::SOLID);
128 
129   if (bfcull || vo == SoShapeHintsElement::UNKNOWN_ORDERING) {
130     SbPlane nearp = SoViewVolumeElement::get(state).getPlane(0.0f);
131     nearp = SbPlane(-nearp.getNormal(), -nearp.getDistanceFromOrigin());
132     // if back face culling is enabled, we can do less work
133     SbVec3f center;
134     for (i = 0; i < n; i++) {
135       int idx = i*3;
136       center.setValue(0.0f, 0.0f, 0.0f);
137       tri.idx = idx;
138       for (int j = 0; j < 3; j++) {
139         tri.backface = 0;
140         v = varray + idx + j;
141         center += v->getPoint();
142       }
143       center /= 3.0f;
144       mm.multVecMatrix(center, center);
145       tri.dist = nearp.getDistance(center);
146       trianglelist->append(tri);
147     }
148   }
149   else {
150     // project each point onto screen to find the vertex
151     // ordering of the triangle. Sort on vertex closest
152     // to the near plane.
153     SbMatrix obj2vp =
154       mm * SoViewingMatrixElement::get(state) *
155       SoProjectionMatrixElement::get(state);
156 
157     int clockwise = (vo == SoShapeHintsElement::CLOCKWISE) ? 1 : 0;
158     SbVec3f c[3];
159     for (i = 0; i < n; i++) {
160       int idx = i*3;
161       tri.idx = idx;
162       // projected coordinates are between -1 and 1
163       float smalldist = 10.0f;
164       for (int j = 0; j < 3; j++) {
165         v = varray + idx + j;
166         c[j] = v->getPoint();
167         obj2vp.multVecMatrix(c[j], c[j]);
168         float dist = c[j][2];
169         if (dist < smalldist) smalldist = dist;
170       }
171       SbVec3f v0 = c[2]-c[0];
172       SbVec3f v1 = c[1]-c[0];
173       // we need only the z-component of the cross product
174       // to determine if triangle is cw or ccw
175       float cz = v0[0]*v1[1] - v0[1]*v1[0];
176       tri.backface = clockwise;
177       if (cz < 0.0f) tri.backface = 1 - clockwise;
178       tri.dist = smalldist;
179       this->trianglelist->append(tri);
180     }
181   }
182 
183   const sorted_triangle * tarray = this->trianglelist->getArrayPtr();
184   qsort((void*)tarray, n, sizeof(sorted_triangle), compare_triangles);
185 
186   int idx;
187 
188   // this rendering loop can be optimized a lot, of course, but speed
189   // is not so important here, since it's slow to generate, copy and
190   // sort the triangles anyway.
191   glBegin(GL_TRIANGLES);
192   for (i = 0; i < n; i++) {
193     idx = tarray[i].idx;
194     v = varray + idx;
195     glTexCoord4fv(v->getTextureCoords().getValue());
196     glNormal3fv(v->getNormal().getValue());
197     mb.send(v->getMaterialIndex(), TRUE);
198     glVertex3fv(v->getPoint().getValue());
199 
200     v = varray + idx+1;
201     glTexCoord4fv(v->getTextureCoords().getValue());
202     glNormal3fv(v->getNormal().getValue());
203     mb.send(v->getMaterialIndex(), TRUE);
204     glVertex3fv(v->getPoint().getValue());
205 
206     v = varray + idx+2;
207     glTexCoord4fv(v->getTextureCoords().getValue());
208     glNormal3fv(v->getNormal().getValue());
209     mb.send(v->getMaterialIndex(), TRUE);
210     glVertex3fv(v->getPoint().getValue());
211   }
212   glEnd();
213 }
214