1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2004 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version. The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * OpenSceneGraph Public License for more details.
12 */
13
14 #include <iostream>
15 #include <sstream>
16 #include <fstream>
17 #include <string>
18 #include <stdio.h>
19 #include <functional>
20
21 #include "obj.h"
22
23 #include <osg/Notify>
24
25 #include <osgDB/FileUtils>
26 #include <osgDB/FileNameUtils>
27
28 #include <string.h>
29
30 using namespace obj;
31
32
33 #ifdef _MSC_VER
34 #define strncasecmp strnicmp
35 #endif
36
37
strip(const std::string & ss)38 static std::string strip( const std::string& ss )
39 {
40 std::string result;
41 result.assign( std::find_if( ss.begin(), ss.end(), std::not1( std::ptr_fun< int, int >( isspace ) ) ),
42 std::find_if( ss.rbegin(), ss.rend(), std::not1( std::ptr_fun< int, int >( isspace ) ) ).base() );
43 return( result );
44 }
45
46 /*
47 * parse a subset of texture options, following
48 * http://local.wasp.uwa.edu.au/~pbourke/dataformats/mtl/
49 */
parseTextureMap(const std::string & ss,Material::Map::TextureMapType type)50 static Material::Map parseTextureMap( const std::string& ss, Material::Map::TextureMapType type)
51 {
52 Material::Map map;
53 std::string s(ss);
54 for (;;)
55 {
56 if (s[0] != '-')
57 break;
58
59 int n;
60 if (s[1] == 's' || s[1] == 'o')
61 {
62 float x, y, z;
63 if (sscanf(s.c_str(), "%*s %f %f %f%n", &x, &y, &z, &n) != 3)
64 {
65 break;
66 }
67
68 if (s[1] == 's')
69 {
70 // texture scale
71 map.uScale = x;
72 map.vScale = y;
73 }
74 else if (s[1] == 'o')
75 {
76 // texture offset
77 map.uOffset = x;
78 map.vOffset = y;
79 }
80 }
81 else if (s.compare(1,2,"mm")==0)
82 {
83 // texture color offset and gain
84 float base, gain;
85 if (sscanf(s.c_str(), "%*s %f %f%n", &base, &gain, &n) != 2)
86 {
87 break;
88 }
89 // UNUSED
90 }
91 else if (s.compare(1,2,"bm")==0)
92 {
93 // blend multiplier
94 float mult;
95 if (sscanf(s.c_str(), "%*s %f%n", &mult, &n) != 2)
96 {
97 break;
98 }
99 // UNUSED
100 }
101 else if (s.compare(1,5,"clamp")==0)
102 {
103 OSG_NOTICE<<"Got Clamp\n";
104 char c[4];
105 if (sscanf(s.c_str(), "%*s %3s%n", c, &n) != 1)
106 {
107 break;
108 }
109 if(strncmp(c,"on",2)==0) map.clamp = true;
110 else map.clamp = false; // default behavioud
111 }
112 else
113 break;
114
115 s = strip(s.substr(n));
116 }
117
118 map.name = osgDB::convertFileNameToNativeStyle(s);
119 map.type = type;
120 return map;
121 }
122
readline(std::istream & fin,char * line,const int LINE_SIZE)123 bool Model::readline(std::istream& fin, char* line, const int LINE_SIZE)
124 {
125 if (LINE_SIZE<1) return false;
126
127 bool eatWhiteSpaceAtStart = true;
128 bool changeTabsToSpaces = true;
129
130 char* ptr = line;
131 char* end = line+LINE_SIZE-1;
132 bool skipNewline = false;
133 while (fin && ptr<end)
134 {
135
136 int c=fin.get();
137 int p=fin.peek();
138 if (c=='\r')
139 {
140 if (p=='\n')
141 {
142 // we have a windows line endings.
143 fin.get();
144 // OSG_NOTICE<<"We have dos line ending"<<std::endl;
145 if (skipNewline)
146 {
147 skipNewline = false;
148 *ptr++ = ' ';
149 continue;
150 }
151 else break;
152 }
153 // we have Mac line ending
154 // OSG_NOTICE<<"We have mac line ending"<<std::endl;
155 if (skipNewline)
156 {
157 skipNewline = false;
158 *ptr++ = ' ';
159 continue;
160 }
161 else break;
162 }
163 else if (c=='\n')
164 {
165 // we have unix line ending.
166 // OSG_NOTICE<<"We have unix line ending"<<std::endl;
167 if (skipNewline)
168 {
169 *ptr++ = ' ';
170 continue;
171 }
172 else break;
173 }
174 else if (c=='\\' && (p=='\r' || p=='\n'))
175 {
176 // need to keep return;
177 skipNewline = true;
178 }
179 else if (c!=std::ifstream::traits_type::eof()) // don't copy eof.
180 {
181 skipNewline = false;
182
183 if (!eatWhiteSpaceAtStart || (c!=' ' && c!='\t'))
184 {
185 eatWhiteSpaceAtStart = false;
186 *ptr++ = c;
187 }
188 }
189
190
191 }
192
193 // strip trailing spaces
194 while (ptr>line && *(ptr-1)==' ')
195 {
196 --ptr;
197 }
198
199 *ptr = 0;
200
201 if (changeTabsToSpaces)
202 {
203
204 for(ptr = line; *ptr != 0; ++ptr)
205 {
206 if (*ptr == '\t') *ptr=' ';
207 }
208 }
209
210 return true;
211 }
212
213
lastComponent(const char * linep)214 std::string Model::lastComponent(const char* linep)
215 {
216 std::string line = std::string(linep);
217 int space = line.find_last_of(" ");
218 if (space >= 0) {
219 line = line.substr(space+1);
220 }
221 return line;
222 }
223
readMTL(std::istream & fin)224 bool Model::readMTL(std::istream& fin)
225 {
226 OSG_INFO<<"Reading MTL file"<<std::endl;
227
228 const int LINE_SIZE = 4096;
229 char line[LINE_SIZE];
230 float r = 1.0f, g = 1.0f, b = 1.0f, a = 1.0f;
231 bool usingDissolve = false;
232
233 Material* material = 0;// &(materialMap[""]);
234 std::string filename;
235
236 while (fin)
237 {
238 readline(fin,line,LINE_SIZE);
239 if (line[0]=='#' || line[0]=='$')
240 {
241 // comment line
242 // OSG_NOTICE <<"Comment: "<<line<<std::endl;
243 }
244 else if (strlen(line)>0)
245 {
246 if (strncasecmp(line,"newmtl ",7)==0)
247 {
248 // get material name and left- and right-trim all the white-space
249 std::string materialName(strip(line+7));
250 material = & materialMap[materialName];
251 material->name = materialName;
252 usingDissolve = false;
253 }
254 else if (material)
255 {
256 if (strncasecmp(line,"Ka ",3)==0)
257 {
258 unsigned int fieldsRead = sscanf(line+3,"%f %f %f %f", &r, &g, &b, &a);
259
260 if (fieldsRead==1)
261 {
262 material->ambient[ 0 ] = r;
263 }
264 else if (fieldsRead==2)
265 {
266 material->ambient[ 0 ] = r;
267 material->ambient[ 1 ] = g;
268 }
269 else if (fieldsRead==3)
270 {
271 material->ambient[ 0 ] = r;
272 material->ambient[ 1 ] = g;
273 material->ambient[ 2 ] = b;
274 }
275 else if (fieldsRead==4)
276 {
277 material->ambient[ 0 ] = r;
278 material->ambient[ 1 ] = g;
279 material->ambient[ 2 ] = b;
280 material->ambient[ 3 ] = a;
281 }
282 }
283 else if (strncasecmp(line,"Kd ",3)==0)
284 {
285 unsigned int fieldsRead = sscanf(line+3,"%f %f %f %f", &r, &g, &b, &a);
286
287 if (fieldsRead==1)
288 {
289 material->diffuse[ 0 ] = r;
290 }
291 else if (fieldsRead==2)
292 {
293 material->diffuse[ 0 ] = r;
294 material->diffuse[ 1 ] = g;
295 }
296 else if (fieldsRead==3)
297 {
298 material->diffuse[ 0 ] = r;
299 material->diffuse[ 1 ] = g;
300 material->diffuse[ 2 ] = b;
301 }
302 else if (fieldsRead==4)
303 {
304 material->diffuse[ 0 ] = r;
305 material->diffuse[ 1 ] = g;
306 material->diffuse[ 2 ] = b;
307 material->diffuse[ 3 ] = a;
308 }
309 }
310 else if (strncasecmp(line,"Ks ",3)==0)
311 {
312 unsigned int fieldsRead = sscanf(line+3,"%f %f %f %f", &r, &g, &b, &a);
313
314 if (fieldsRead==1)
315 {
316 material->specular[ 0 ] = r;
317 }
318 else if (fieldsRead==2)
319 {
320 material->specular[ 0 ] = r;
321 material->specular[ 1 ] = g;
322 }
323 else if (fieldsRead==3)
324 {
325 material->specular[ 0 ] = r;
326 material->specular[ 1 ] = g;
327 material->specular[ 2 ] = b;
328 }
329 else if (fieldsRead==4)
330 {
331 material->specular[ 0 ] = r;
332 material->specular[ 1 ] = g;
333 material->specular[ 2 ] = b;
334 material->specular[ 3 ] = a;
335 }
336 }
337 else if (strncasecmp(line,"Ke ",3)==0)
338 {
339 unsigned int fieldsRead = sscanf(line+3,"%f %f %f %f", &r, &g, &b, &a);
340
341 if (fieldsRead==1)
342 {
343 material->emissive[ 0 ] = r;
344 }
345 else if (fieldsRead==2)
346 {
347 material->emissive[ 0 ] = r;
348 material->emissive[ 1 ] = g;
349 }
350 else if (fieldsRead==3)
351 {
352 material->emissive[ 0 ] = r;
353 material->emissive[ 1 ] = g;
354 material->emissive[ 2 ] = b;
355 }
356 else if (fieldsRead==4)
357 {
358 material->emissive[ 0 ] = r;
359 material->emissive[ 1 ] = g;
360 material->emissive[ 2 ] = b;
361 material->emissive[ 3 ] = a;
362 }
363 }
364 else if (strncasecmp(line,"Tf ",3)==0)
365 {
366 unsigned int fieldsRead = sscanf(line+3,"%f %f %f %f", &r, &g, &b, &a);
367
368 if (fieldsRead==1)
369 {
370 material->Tf[ 0 ] = r;
371 }
372 else if (fieldsRead==2)
373 {
374 material->Tf[ 0 ] = r;
375 material->Tf[ 1 ] = g;
376 }
377 else if (fieldsRead==3)
378 {
379 material->Tf[ 0 ] = r;
380 material->Tf[ 1 ] = g;
381 material->Tf[ 2 ] = b;
382 }
383 else if (fieldsRead==4)
384 {
385 material->Tf[ 0 ] = r;
386 material->Tf[ 1 ] = g;
387 material->Tf[ 2 ] = b;
388 material->Tf[ 3 ] = a;
389 }
390 }
391 else if (strncasecmp(line,"sharpness ",10)==0)
392 {
393 float sharpness = 0.0f;
394 unsigned int fieldsRead = sscanf(line+10,"%f", &sharpness);
395
396 if (fieldsRead==1) material->sharpness = sharpness;
397 }
398 else if (strncasecmp(line,"illum ",6)==0)
399 {
400 int illum = 0;
401 unsigned int fieldsRead = sscanf(line+6,"%d", &illum);
402
403 if (fieldsRead==1) material->illum = illum;
404 }
405 else if (strncasecmp(line,"Ns ",3)==0)
406 {
407 int Ns = 0;
408 unsigned int fieldsRead = sscanf(line+3,"%d", &Ns);
409
410 if (fieldsRead==1) material->Ns = Ns;
411 }
412 else if (strncasecmp(line,"Ni ",3)==0)
413 {
414 int Ni = 0;
415 unsigned int fieldsRead = sscanf(line+3,"%d", &Ni);
416
417 if (fieldsRead==1) material->Ni = Ni;
418 }
419 //
420 // Tr - transparency
421 //
422 // Seems that the world did not agreed about the specification of the item.
423 //
424 // Some thinks that value of 1 means opaque material and 0 transparent material,
425 // such as http://people.sc.fsu.edu/~jburkardt/data/mtl/mtl.html .
426 //
427 // However, 3ds Max export uses the opposite: 0 means opaque material and
428 // 1 completely transparent material. These 3ds Max exported files
429 // carry the following signature as the first line in the file (*.obj, *.mtl):
430 // # 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
431 //
432 // Moreover, at least one model uses Tr followed by two numbers.
433 // Such model can be downloaded from http://graphics.cs.williams.edu/data/meshes/cube.zip
434 // (part of the following collection: http://graphics.cs.williams.edu/data/meshes.xml).
435 //
436 // Current solution: As we do not know what is the correct interpretation of
437 // the value 0 and value 1 for Tr, we will rely on d (dissolve) parameter instead
438 // whenever it is present. This seems to fix the problem on large number of models.
439 //
440 else if (strncasecmp(line,"Tr ",3)==0)
441 {
442 if( !usingDissolve )
443 {
444 float alpha=1.0f;
445 unsigned int fieldsRead = sscanf(line+3,"%f", &alpha);
446
447 if (fieldsRead==1)
448 {
449 material->ambient[3] = alpha;
450 material->diffuse[3] = alpha;
451 material->specular[3] = alpha;
452 material->emissive[3] = alpha;
453 }
454 }
455 }
456 //
457 // d - dissolve (pseudo-transparency)
458 //
459 // Dissolve of value 1 means completely opaque material
460 // and value of 0 results in completely transparent material.
461 //
462 // To be compatible with 3D Max obj exporter,
463 // d takes precedence over Tr (handled through usingDissolve variable).
464 //
465 else if (strncasecmp(line,"d ",2)==0)
466 {
467 float alpha=1.0f;
468 unsigned int fieldsRead = sscanf(line+2,"%f", &alpha);
469
470 if (fieldsRead==1)
471 {
472 material->ambient[3] = alpha;
473 material->diffuse[3] = alpha;
474 material->specular[3] = alpha;
475 material->emissive[3] = alpha;
476 usingDissolve = true;
477 }
478 }
479 else if (strncasecmp(line,"map_Ka ",7)==0)
480 {
481 material->maps.push_back(parseTextureMap(strip(line+7),Material::Map::AMBIENT));
482 }
483 // diffuse map
484 else if (strncasecmp(line,"map_Kd ",7)==0)
485 {
486 material->maps.push_back(parseTextureMap(strip(line+7),Material::Map::DIFFUSE));
487 }
488 // specular colour/level map
489 else if (strncasecmp(line,"map_Ks ",7)==0)
490 {
491 material->maps.push_back(parseTextureMap(strip(line+7),Material::Map::SPECULAR));
492 }
493 // map_opacity doesn't exist in the spec, but was already in the plugin
494 // so leave it or plugin will break for some users
495 else if (strncasecmp(line,"map_opacity ",12)==0)
496 {
497 material->maps.push_back(parseTextureMap(strip(line+12),Material::Map::OPACITY));
498 }
499 // proper dissolve/opacity map
500 else if (strncasecmp(line,"map_d ",6)==0)
501 {
502 material->maps.push_back(parseTextureMap(strip(line+6),Material::Map::OPACITY));
503 }
504 // specular exponent map
505 else if (strncasecmp(line,"map_Ns ",7)==0)
506 {
507 material->maps.push_back(parseTextureMap(strip(line+7),Material::Map::SPECULAR_EXPONENT));
508 }
509 // modelling tools and convertors variously produce bump, map_bump, and map_Bump so parse them all
510 else if (strncasecmp(line,"bump ",5)==0 || strncasecmp(line,"map_bump ",9)==0)
511 {
512 material->maps.push_back(parseTextureMap(strip(line+5),Material::Map::BUMP));
513 }
514 else if (strncasecmp(line,"map_bump ",9)==0)
515 {
516 material->maps.push_back(parseTextureMap(strip(line+9),Material::Map::BUMP));
517 }
518 else if (strncasecmp(line,"map_Bump ",9)==0)
519 {
520 material->maps.push_back(parseTextureMap(strip(line+9),Material::Map::BUMP));
521 }
522 // displacement map
523 else if (strncasecmp(line,"disp ",5)==0)
524 {
525 material->maps.push_back(parseTextureMap(strip(line+5),Material::Map::DISPLACEMENT));
526 }
527 // reflection map (the original code had the possibility of a blank "refl" line
528 // which isn't correct according to the spec, so this bit might break for some
529 // modelling packages...
530 else if (strncasecmp(line,"refl ",5)==0)
531 {
532 material->maps.push_back(parseTextureMap(strip(line+5),Material::Map::REFLECTION));
533 }
534 else
535 {
536 OSG_NOTICE <<"*** line not handled *** :"<<line<<std::endl;
537 }
538 }
539 else
540 {
541 OSG_NOTICE <<"*** line not handled *** :"<<line<<std::endl;
542 }
543
544 }
545
546 }
547
548 return true;
549 }
550
trim(const std::string & s)551 std::string trim(const std::string& s)
552 {
553 if(s.length() == 0)
554 return s;
555 int b = s.find_first_not_of(" \t");
556 int e = s.find_last_not_of(" \t");
557 if(b == -1) // No non-spaces
558 return "";
559 return std::string(s, b, e - b + 1);
560 }
561
isZBrushColorField(char * line)562 inline bool isZBrushColorField(char* line)
563 {
564 return strncmp(line, "#MRGB", 5) == 0;
565 }
566
readOBJ(std::istream & fin,const osgDB::ReaderWriter::Options * options)567 bool Model::readOBJ(std::istream& fin, const osgDB::ReaderWriter::Options* options)
568 {
569 OSG_INFO<<"Reading OBJ file"<<std::endl;
570
571 const int LINE_SIZE = 4096;
572 char line[LINE_SIZE];
573 float x = 0.0f, y = 0.0f, z = 0.0f, w = 0.0f;
574 float r,g,b,a;
575
576 while (fin)
577 {
578 readline(fin,line,LINE_SIZE);
579 if ((line[0]=='#' && !isZBrushColorField(line)) || line[0]=='$')
580 {
581 // comment line
582 // OSG_NOTICE <<"Comment: "<<line<<std::endl;
583 }
584 else if(isZBrushColorField(line))
585 {
586 // Get the zBrush vertex colors given in comments under the form :
587 // * #MRGB MMRRGGBB MMRRGGBB ... (up to 64 hexadecimal color fields)
588 std::string colorFields(line + 6);
589 while (colorFields.size() >= 8)
590 {
591 std::string currentValue;
592
593 // Skipping the MM component
594 colorFields = colorFields.substr(2);
595
596 currentValue = colorFields.substr(0,2);
597 r = static_cast<float>(strtol(currentValue.c_str(), NULL, 16)) / 255.;
598 colorFields = colorFields.substr(2);
599
600 currentValue = colorFields.substr(0,2);
601 g = static_cast<float>(strtol(currentValue.c_str(), NULL, 16)) / 255.;
602 colorFields = colorFields.substr(2);
603
604 currentValue = colorFields.substr(0,2);
605 b = static_cast<float>(strtol(currentValue.c_str(), NULL, 16)) / 255.;
606 colorFields = colorFields.substr(2);
607
608 colors.push_back(osg::Vec4(r, g, b, 1.0));
609 }
610 }
611 else if (strlen(line)>0)
612 {
613 if (strncmp(line,"v ",2)==0)
614 {
615 unsigned int fieldsRead = sscanf(line+2,"%f %f %f %f %f %f %f", &x, &y, &z, &w, &g, &b, &a);
616
617 if (fieldsRead==1)
618 vertices.push_back(osg::Vec3(x,0.0f,0.0f));
619 else if (fieldsRead==2)
620 vertices.push_back(osg::Vec3(x,y,0.0f));
621 else if (fieldsRead==3)
622 vertices.push_back(osg::Vec3(x,y,z));
623 else if (fieldsRead == 4)
624 vertices.push_back(osg::Vec3(x/w,y/w,z/w));
625 else if (fieldsRead == 6)
626 {
627 vertices.push_back(osg::Vec3(x,y,z));
628 colors.push_back(osg::Vec4(w, g, b, 1.0));
629 }
630 else if ( fieldsRead == 7 )
631 {
632 vertices.push_back(osg::Vec3(x,y,z));
633 colors.push_back(osg::Vec4(w, g, b, a));
634 }
635 }
636 else if (strncmp(line,"vn ",3)==0)
637 {
638 unsigned int fieldsRead = sscanf(line+3,"%f %f %f", &x, &y, &z);
639
640 if (fieldsRead==1) normals.push_back(osg::Vec3(x,0.0f,0.0f));
641 else if (fieldsRead==2) normals.push_back(osg::Vec3(x,y,0.0f));
642 else if (fieldsRead==3) normals.push_back(osg::Vec3(x,y,z));
643 }
644 else if (strncmp(line,"vt ",3)==0)
645 {
646 unsigned int fieldsRead = sscanf(line+3,"%f %f %f", &x, &y, &z);
647
648 if (fieldsRead==1) texcoords.push_back(osg::Vec2(x,0.0f));
649 else if (fieldsRead==2) texcoords.push_back(osg::Vec2(x,y));
650 else if (fieldsRead==3) texcoords.push_back(osg::Vec2(x,y));
651 }
652 else if (strncmp(line,"l ",2)==0 ||
653 strncmp(line,"p ",2)==0 ||
654 strncmp(line,"f ",2)==0)
655 {
656 char* ptr = line+2;
657
658 Element* element = new Element( (line[0]=='p') ? Element::POINTS :
659 (line[0]=='l') ? Element::POLYLINE :
660 Element::POLYGON );
661
662 // OSG_NOTICE<<"face"<<ptr<<std::endl;
663
664 int vi=0, ti=0, ni=0;
665 while(*ptr!=0)
666 {
667 // skip white space
668 while(*ptr==' ') ++ptr;
669
670 if (sscanf(ptr, "%d/%d/%d", &vi, &ti, &ni) == 3)
671 {
672 // OSG_NOTICE<<" vi="<<vi<<"/ti="<<ti<<"/ni="<<ni<<std::endl;
673 element->vertexIndices.push_back(remapVertexIndex(vi));
674 element->normalIndices.push_back(remapNormalIndex(ni));
675 element->texCoordIndices.push_back(remapTexCoordIndex(ti));
676 }
677 else if (sscanf(ptr, "%d//%d", &vi, &ni) == 2)
678 {
679 // OSG_NOTICE<<" vi="<<vi<<"//ni="<<ni<<std::endl;
680 element->vertexIndices.push_back(remapVertexIndex(vi));
681 if (remapNormalIndex(ni) < static_cast<int>(normals.size()))
682 element->normalIndices.push_back(remapNormalIndex(ni));
683 }
684 else if (sscanf(ptr, "%d/%d", &vi, &ti) == 2)
685 {
686 // OSG_NOTICE<<" vi="<<vi<<"/ti="<<ti<<std::endl;
687 element->vertexIndices.push_back(remapVertexIndex(vi));
688 if (remapTexCoordIndex(ti) < static_cast<int>(texcoords.size()))
689 element->texCoordIndices.push_back(remapTexCoordIndex(ti));
690 }
691 else if (sscanf(ptr, "%d", &vi) == 1)
692 {
693 // OSG_NOTICE<<" vi="<<vi<<std::endl;
694 element->vertexIndices.push_back(remapVertexIndex(vi));
695 }
696
697 // skip to white space or end of line
698 while(*ptr!=' ' && *ptr!=0) ++ptr;
699
700 }
701
702 if (!element->normalIndices.empty() && element->normalIndices.size() != element->vertexIndices.size())
703 {
704 element->normalIndices.clear();
705 }
706
707 if (!element->texCoordIndices.empty() && element->texCoordIndices.size() != element->vertexIndices.size())
708 {
709 element->texCoordIndices.clear();
710 }
711
712 if (!element->vertexIndices.empty())
713 {
714 Element::CoordinateCombination coordateCombination = element->getCoordinateCombination();
715 if (coordateCombination!=currentElementState.coordinateCombination)
716 {
717 currentElementState.coordinateCombination = coordateCombination;
718 currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
719 }
720 addElement(element);
721 }
722 else
723 {
724 // empty element, don't both adding, just unref to delete it.
725 element->unref();
726 }
727
728 }
729 else if (strncmp(line,"usemtl ",7)==0)
730 {
731 std::string materialName( line+7 );
732 if (currentElementState.materialName != materialName)
733 {
734 currentElementState.materialName = materialName;
735 currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
736 }
737 }
738 else if (strncmp(line,"mtllib ",7)==0)
739 {
740 std::string materialFileName = trim( line+7 );
741 std::string fullPathFileName = osgDB::findDataFile( materialFileName, options );
742 if (!fullPathFileName.empty())
743 {
744 osgDB::ifstream mfin( fullPathFileName.c_str() );
745 if (mfin)
746 {
747 OSG_INFO << "Obj reading mtllib '" << fullPathFileName << "'\n";
748 readMTL(mfin);
749 }
750 else
751 {
752 OSG_WARN << "Obj unable to load mtllib '" << fullPathFileName << "'\n";
753 }
754 }
755 else
756 {
757 OSG_WARN << "Obj unable to find mtllib '" << materialFileName << "'\n";
758 }
759 }
760 else if (strncmp(line,"o ",2)==0)
761 {
762 std::string objectName(line+2);
763 if (currentElementState.objectName != objectName)
764 {
765 currentElementState.objectName = objectName;
766 currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
767 }
768 }
769 else if (strcmp(line,"o")==0)
770 {
771 std::string objectName(""); // empty name
772 if (currentElementState.objectName != objectName)
773 {
774 currentElementState.objectName = objectName;
775 currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
776 }
777 }
778 else if (strncmp(line,"g ",2)==0)
779 {
780 std::string groupName(line+2);
781 if (currentElementState.groupName != groupName)
782 {
783 currentElementState.groupName = groupName;
784 currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
785 }
786 }
787 else if (strcmp(line,"g")==0)
788 {
789 std::string groupName(""); // empty name
790 if (currentElementState.groupName != groupName)
791 {
792 currentElementState.groupName = groupName;
793 currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
794 }
795 }
796 else if (strncmp(line,"s ",2)==0)
797 {
798 int smoothingGroup=0;
799 if (strncmp(line+2,"off",3)==0) smoothingGroup = 0;
800 else sscanf(line+2,"%d",&smoothingGroup);
801
802 if (currentElementState.smoothingGroup != smoothingGroup)
803 {
804 currentElementState.smoothingGroup = smoothingGroup;
805 currentElementList = 0; // reset the element list to force a recompute of which ElementList to use
806 }
807 }
808 else
809 {
810 OSG_NOTICE <<"*** line not handled *** :"<<line<<std::endl;
811 }
812
813 }
814
815 }
816 #if 0
817 OSG_NOTICE <<"vertices :"<<vertices.size()<<std::endl;
818 OSG_NOTICE <<"normals :"<<normals.size()<<std::endl;
819 OSG_NOTICE <<"texcoords :"<<texcoords.size()<<std::endl;
820 OSG_NOTICE <<"materials :"<<materialMap.size()<<std::endl;
821 OSG_NOTICE <<"elementStates :"<<elementStateMap.size()<<std::endl;
822
823 unsigned int pos=0;
824 for(ElementStateMap::iterator itr=elementStateMap.begin();
825 itr!=elementStateMap.end();
826 ++itr,++pos)
827 {
828 const ElementState& es = itr->first;
829 ElementList& el = itr->second;
830 OSG_NOTICE<<"ElementState "<<pos<<std::endl;
831 OSG_NOTICE<<" es.objectName="<<es.objectName<<std::endl;
832 OSG_NOTICE<<" es.groupName="<<es.groupName<<std::endl;
833 OSG_NOTICE<<" es.materialName="<<es.materialName<<std::endl;
834 OSG_NOTICE<<" es.smoothGroup="<<es.smoothingGroup<<std::endl;
835 OSG_NOTICE<<" ElementList ="<<el.size()<<std::endl;
836
837 }
838 #endif
839 return true;
840 }
841
842
addElement(Element * element)843 void Model::addElement(Element* element)
844 {
845 if (!currentElementList)
846 {
847 currentElementList = & (elementStateMap[currentElementState]);
848 }
849 currentElementList->push_back(element);
850
851 }
852
averageNormal(const Element & element) const853 osg::Vec3 Model::averageNormal(const Element& element) const
854 {
855 osg::Vec3 normal;
856 for(Element::IndexList::const_iterator itr=element.normalIndices.begin();
857 itr!=element.normalIndices.end();
858 ++itr)
859 {
860 normal += normals[*itr];
861 }
862 normal.normalize();
863
864 return normal;
865 }
866
computeNormal(const Element & element) const867 osg::Vec3 Model::computeNormal(const Element& element) const
868 {
869 osg::Vec3 normal;
870 for(unsigned int i=0;i<element.vertexIndices.size()-2;++i)
871 {
872 osg::Vec3 a = vertices[element.vertexIndices[i]];
873 osg::Vec3 b = vertices[element.vertexIndices[i+1]];
874 osg::Vec3 c = vertices[element.vertexIndices[i+2]];
875 osg::Vec3 localNormal = (b-a) ^(c-b);
876 normal += localNormal;
877 }
878 normal.normalize();
879
880 return normal;
881 }
882
needReverse(const Element & element) const883 bool Model::needReverse(const Element& element) const
884 {
885 if (element.normalIndices.empty()) return false;
886
887 return computeNormal(element)*averageNormal(element) < 0.0f;
888 }
889