1 #include <osgUtil/TangentSpaceGenerator>
2 
3 #include <osg/Notify>
4 #include <osg/io_utils>
5 
6 using namespace osgUtil;
7 
TangentSpaceGenerator()8 TangentSpaceGenerator::TangentSpaceGenerator()
9 :    osg::Referenced(),
10     T_(new osg::Vec4Array),
11     B_(new osg::Vec4Array),
12     N_(new osg::Vec4Array)
13 {
14     T_->setBinding(osg::Array::BIND_PER_VERTEX); T_->setNormalize(false);
15     B_->setBinding(osg::Array::BIND_PER_VERTEX); B_->setNormalize(false);
16     N_->setBinding(osg::Array::BIND_PER_VERTEX); N_->setNormalize(false);
17 }
18 
TangentSpaceGenerator(const TangentSpaceGenerator & copy,const osg::CopyOp & copyop)19 TangentSpaceGenerator::TangentSpaceGenerator(const TangentSpaceGenerator &copy, const osg::CopyOp &copyop)
20 :    osg::Referenced(copy),
21     T_(static_cast<osg::Vec4Array *>(copyop(copy.T_.get()))),
22     B_(static_cast<osg::Vec4Array *>(copyop(copy.B_.get()))),
23     N_(static_cast<osg::Vec4Array *>(copyop(copy.N_.get())))
24 {
25 }
26 
generate(osg::Geometry * geo,int normal_map_tex_unit)27 void TangentSpaceGenerator::generate(osg::Geometry *geo, int normal_map_tex_unit)
28 {
29     const osg::Array *vx = geo->getVertexArray();
30     const osg::Array *nx = geo->getNormalArray();
31     const osg::Array *tx = geo->getTexCoordArray(normal_map_tex_unit);
32 
33     if (!vx || !tx) return;
34 
35 
36     unsigned int vertex_count = vx->getNumElements();
37     T_->assign(vertex_count, osg::Vec4());
38     B_->assign(vertex_count, osg::Vec4());
39     N_->assign(vertex_count, osg::Vec4());
40 
41     unsigned int i; // VC6 doesn't like for-scoped variables
42 
43     for (unsigned int pri=0; pri<geo->getNumPrimitiveSets(); ++pri) {
44         osg::PrimitiveSet *pset = geo->getPrimitiveSet(pri);
45 
46         unsigned int N = pset->getNumIndices();
47 
48         switch (pset->getMode()) {
49 
50             case osg::PrimitiveSet::TRIANGLES:
51                 for (i=0; i<N; i+=3) {
52                     compute(pset, vx, nx, tx, i, i+1, i+2);
53                 }
54                 break;
55 
56             case osg::PrimitiveSet::QUADS:
57                 for (i=0; i<N; i+=4) {
58                     compute(pset, vx, nx, tx, i, i+1, i+2);
59                     compute(pset, vx, nx, tx, i+2, i+3, i);
60                 }
61                 break;
62 
63             case osg::PrimitiveSet::TRIANGLE_STRIP:
64                 if (pset->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType) {
65                     osg::DrawArrayLengths *dal = static_cast<osg::DrawArrayLengths *>(pset);
66                     unsigned int j = 0;
67                     for (osg::DrawArrayLengths::const_iterator pi=dal->begin(); pi!=dal->end(); ++pi) {
68                         unsigned int iN = static_cast<unsigned int>(*pi-2);
69                         for (i=0; i<iN; ++i, ++j) {
70                             if ((i%2) == 0) {
71                                 compute(pset, vx, nx, tx, j, j+1, j+2);
72                             } else {
73                                 compute(pset, vx, nx, tx, j+1, j, j+2);
74                             }
75                         }
76                         j += 2;
77                     }
78                 } else {
79                     for (i=0; i<N-2; ++i) {
80                         if ((i%2) == 0) {
81                             compute(pset, vx, nx, tx, i, i+1, i+2);
82                         } else {
83                             compute(pset, vx, nx, tx, i+1, i, i+2);
84                         }
85                     }
86                 }
87                 break;
88 
89             case osg::PrimitiveSet::QUAD_STRIP:
90                 if (pset->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType) {
91                     osg::DrawArrayLengths *dal = static_cast<osg::DrawArrayLengths *>(pset);
92                     unsigned int j = 0;
93                     for (osg::DrawArrayLengths::const_iterator pi=dal->begin(); pi!=dal->end(); ++pi) {
94                         unsigned int iN = static_cast<unsigned int>(*pi-2);
95                         for (i=0; i<iN; ++i, ++j) {
96                             if ((i%2) == 0) {
97                                 compute(pset, vx, nx, tx, j, j+2, j+1);
98                             } else {
99                                 compute(pset, vx, nx, tx, j, j+1, j+2);
100                             }
101                         }
102                         j += 2;
103                     }
104                 } else {
105                     for (i=0; i<N-2; ++i) {
106                         if ((i%2) == 0) {
107                             compute(pset, vx, nx, tx, i, i+2, i+1);
108                         } else {
109                             compute(pset, vx, nx, tx, i, i+1, i+2);
110                         }
111                     }
112                 }
113                 break;
114 
115             case osg::PrimitiveSet::TRIANGLE_FAN:
116             case osg::PrimitiveSet::POLYGON:
117                 if (pset->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType) {
118                     osg::DrawArrayLengths *dal = static_cast<osg::DrawArrayLengths *>(pset);
119                     unsigned int j = 0;
120                     for (osg::DrawArrayLengths::const_iterator pi=dal->begin(); pi!=dal->end(); ++pi) {
121                         unsigned int iN = static_cast<unsigned int>(*pi-2);
122                         for (i=0; i<iN; ++i) {
123                             compute(pset, vx, nx, tx, 0, j+1, j+2);
124                         }
125                         j += 2;
126                     }
127                 } else {
128                     for (i=0; i<N-2; ++i) {
129                         compute(pset, vx, nx, tx, 0, i+1, i+2);
130                     }
131                 }
132                 break;
133 
134             case osg::PrimitiveSet::POINTS:
135             case osg::PrimitiveSet::LINES:
136             case osg::PrimitiveSet::LINE_STRIP:
137             case osg::PrimitiveSet::LINE_LOOP:
138             case osg::PrimitiveSet::LINES_ADJACENCY:
139             case osg::PrimitiveSet::LINE_STRIP_ADJACENCY:
140                 break;
141 
142             default: OSG_WARN << "Warning: TangentSpaceGenerator: unknown primitive mode " << pset->getMode() << "\n";
143         }
144     }
145 
146     // normalize basis vectors and force the normal vector to match
147     // the triangle normal's direction
148     unsigned int attrib_count = vx->getNumElements();
149     for (i=0; i<attrib_count; ++i) {
150         osg::Vec4 &vT = (*T_)[i];
151         osg::Vec4 &vB = (*B_)[i];
152         osg::Vec4 &vN = (*N_)[i];
153 
154         osg::Vec3 txN = osg::Vec3(vT.x(), vT.y(), vT.z()) ^ osg::Vec3(vB.x(), vB.y(), vB.z());
155         bool flipped = txN * osg::Vec3(vN.x(), vN.y(), vN.z()) < 0;
156 
157         if (flipped) {
158             vN = osg::Vec4(-txN, 0);
159         } else {
160             vN = osg::Vec4(txN, 0);
161         }
162 
163         vT.normalize();
164         vB.normalize();
165         vN.normalize();
166 
167         vT[3] = flipped ? -1.0f : 1.0f;
168     }
169     /* TO-DO: if indexed, compress the attributes to have only one
170      * version of each (different indices for each one?) */
171 }
172 
compute(osg::PrimitiveSet * pset,const osg::Array * vx,const osg::Array * nx,const osg::Array * tx,int iA,int iB,int iC)173 void TangentSpaceGenerator::compute(osg::PrimitiveSet *pset,
174                                     const osg::Array* vx,
175                                     const osg::Array* nx,
176                                     const osg::Array* tx,
177                                     int iA, int iB, int iC)
178 {
179     iA = pset->index(iA);
180     iB = pset->index(iB);
181     iC = pset->index(iC);
182 
183     osg::Vec3 P1;
184     osg::Vec3 P2;
185     osg::Vec3 P3;
186 
187     int i; // VC6 doesn't like for-scoped variables
188 
189     switch (vx->getType())
190     {
191     case osg::Array::Vec2ArrayType:
192         for (i=0; i<2; ++i) {
193             P1.ptr()[i] = static_cast<const osg::Vec2Array&>(*vx)[iA].ptr()[i];
194             P2.ptr()[i] = static_cast<const osg::Vec2Array&>(*vx)[iB].ptr()[i];
195             P3.ptr()[i] = static_cast<const osg::Vec2Array&>(*vx)[iC].ptr()[i];
196         }
197         break;
198 
199     case osg::Array::Vec3ArrayType:
200         P1 = static_cast<const osg::Vec3Array&>(*vx)[iA];
201         P2 = static_cast<const osg::Vec3Array&>(*vx)[iB];
202         P3 = static_cast<const osg::Vec3Array&>(*vx)[iC];
203         break;
204 
205     case osg::Array::Vec4ArrayType:
206         for (i=0; i<3; ++i) {
207             P1.ptr()[i] = static_cast<const osg::Vec4Array&>(*vx)[iA].ptr()[i];
208             P2.ptr()[i] = static_cast<const osg::Vec4Array&>(*vx)[iB].ptr()[i];
209             P3.ptr()[i] = static_cast<const osg::Vec4Array&>(*vx)[iC].ptr()[i];
210         }
211         break;
212 
213     default:
214         OSG_WARN << "Warning: TangentSpaceGenerator: vertex array must be Vec2Array, Vec3Array or Vec4Array" << std::endl;
215     }
216 
217     osg::Vec3 N1;
218     osg::Vec3 N2;
219     osg::Vec3 N3;
220 
221     if(nx)
222     {
223         switch (nx->getType())
224         {
225         case osg::Array::Vec2ArrayType:
226             for (i=0; i<2; ++i) {
227                 N1.ptr()[i] = static_cast<const osg::Vec2Array&>(*nx)[iA].ptr()[i];
228                 N2.ptr()[i] = static_cast<const osg::Vec2Array&>(*nx)[iB].ptr()[i];
229                 N3.ptr()[i] = static_cast<const osg::Vec2Array&>(*nx)[iC].ptr()[i];
230             }
231             break;
232 
233         case osg::Array::Vec3ArrayType:
234             N1 = static_cast<const osg::Vec3Array&>(*nx)[iA];
235             N2 = static_cast<const osg::Vec3Array&>(*nx)[iB];
236             N3 = static_cast<const osg::Vec3Array&>(*nx)[iC];
237             break;
238 
239         case osg::Array::Vec4ArrayType:
240             for (i=0; i<3; ++i) {
241                 N1.ptr()[i] = static_cast<const osg::Vec4Array&>(*nx)[iA].ptr()[i];
242                 N2.ptr()[i] = static_cast<const osg::Vec4Array&>(*nx)[iB].ptr()[i];
243                 N3.ptr()[i] = static_cast<const osg::Vec4Array&>(*nx)[iC].ptr()[i];
244             }
245             break;
246 
247         default:
248             OSG_WARN << "Warning: TangentSpaceGenerator: normal array must be Vec2Array, Vec3Array or Vec4Array" << std::endl;
249         }
250     }
251 
252     osg::Vec2 uv1;
253     osg::Vec2 uv2;
254     osg::Vec2 uv3;
255 
256     switch (tx->getType())
257     {
258     case osg::Array::Vec2ArrayType:
259         uv1 = static_cast<const osg::Vec2Array&>(*tx)[iA];
260         uv2 = static_cast<const osg::Vec2Array&>(*tx)[iB];
261         uv3 = static_cast<const osg::Vec2Array&>(*tx)[iC];
262         break;
263 
264     case osg::Array::Vec3ArrayType:
265         for (i=0; i<2; ++i) {
266             uv1.ptr()[i] = static_cast<const osg::Vec3Array&>(*tx)[iA].ptr()[i];
267             uv2.ptr()[i] = static_cast<const osg::Vec3Array&>(*tx)[iB].ptr()[i];
268             uv3.ptr()[i] = static_cast<const osg::Vec3Array&>(*tx)[iC].ptr()[i];
269         }
270         break;
271 
272     case osg::Array::Vec4ArrayType:
273         for (i=0; i<2; ++i) {
274             uv1.ptr()[i] = static_cast<const osg::Vec4Array&>(*tx)[iA].ptr()[i];
275             uv2.ptr()[i] = static_cast<const osg::Vec4Array&>(*tx)[iB].ptr()[i];
276             uv3.ptr()[i] = static_cast<const osg::Vec4Array&>(*tx)[iC].ptr()[i];
277         }
278         break;
279 
280     default:
281         OSG_WARN << "Warning: TangentSpaceGenerator: texture coord array must be Vec2Array, Vec3Array or Vec4Array" << std::endl;
282     }
283 
284     osg::Vec3 V, T1, T2, T3, B1, B2, B3;
285 
286     // no normal per vertex use the one by face
287     if (!nx) {
288         N1 = (P2 - P1) ^ (P3 - P1);
289         N2 = N1;
290         N3 = N1;
291     }
292 
293     V = osg::Vec3(P2.x() - P1.x(), uv2.x() - uv1.x(), uv2.y() - uv1.y()) ^
294         osg::Vec3(P3.x() - P1.x(), uv3.x() - uv1.x(), uv3.y() - uv1.y());
295     if (V.x() != 0) {
296         V.normalize();
297         T1.x() += -V.y() / V.x();
298         B1.x() += -V.z() / V.x();
299         T2.x() += -V.y() / V.x();
300         B2.x() += -V.z() / V.x();
301         T3.x() += -V.y() / V.x();
302         B3.x() += -V.z() / V.x();
303     }
304 
305     V = osg::Vec3(P2.y() - P1.y(), uv2.x() - uv1.x(), uv2.y() - uv1.y()) ^
306         osg::Vec3(P3.y() - P1.y(), uv3.x() - uv1.x(), uv3.y() - uv1.y());
307     if (V.x() != 0) {
308         V.normalize();
309         T1.y() += -V.y() / V.x();
310         B1.y() += -V.z() / V.x();
311         T2.y() += -V.y() / V.x();
312         B2.y() += -V.z() / V.x();
313         T3.y() += -V.y() / V.x();
314         B3.y() += -V.z() / V.x();
315     }
316 
317     V = osg::Vec3(P2.z() - P1.z(), uv2.x() - uv1.x(), uv2.y() - uv1.y()) ^
318         osg::Vec3(P3.z() - P1.z(), uv3.x() - uv1.x(), uv3.y() - uv1.y());
319     if (V.x() != 0) {
320         V.normalize();
321         T1.z() += -V.y() / V.x();
322         B1.z() += -V.z() / V.x();
323         T2.z() += -V.y() / V.x();
324         B2.z() += -V.z() / V.x();
325         T3.z() += -V.y() / V.x();
326         B3.z() += -V.z() / V.x();
327     }
328 
329     osg::Vec3 tempvec;
330 
331     tempvec = N1 ^ T1;
332     (*T_)[iA] += osg::Vec4(tempvec ^ N1, 0);
333 
334     tempvec = B1 ^ N1;
335     (*B_)[iA] += osg::Vec4(N1 ^ tempvec, 0);
336 
337     tempvec = N2 ^ T2;
338     (*T_)[iB] += osg::Vec4(tempvec ^ N2, 0);
339 
340     tempvec = B2 ^ N2;
341     (*B_)[iB] += osg::Vec4(N2 ^ tempvec, 0);
342 
343     tempvec = N3 ^ T3;
344     (*T_)[iC] += osg::Vec4(tempvec ^ N3, 0);
345 
346     tempvec = B3 ^ N3;
347     (*B_)[iC] += osg::Vec4(N3 ^ tempvec, 0);
348 
349     (*N_)[iA] += osg::Vec4(N1, 0);
350     (*N_)[iB] += osg::Vec4(N2, 0);
351     (*N_)[iC] += osg::Vec4(N3, 0);
352 
353 }
354 
355