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