1 //
2 //   Copyright 2013 Pixar
3 //
4 //   Licensed under the Apache License, Version 2.0 (the "Apache License")
5 //   with the following modification; you may not use this file except in
6 //   compliance with the Apache License and the following modification to it:
7 //   Section 6. Trademarks. is deleted and replaced with:
8 //
9 //   6. Trademarks. This License does not grant permission to use the trade
10 //      names, trademarks, service marks, or product names of the Licensor
11 //      and its affiliates, except as required to comply with Section 4(c) of
12 //      the License and to reproduce the content of the NOTICE file.
13 //
14 //   You may obtain a copy of the Apache License at
15 //
16 //       http://www.apache.org/licenses/LICENSE-2.0
17 //
18 //   Unless required by applicable law or agreed to in writing, software
19 //   distributed under the Apache License with the above modification is
20 //   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 //   KIND, either express or implied. See the Apache License for the specific
22 //   language governing permissions and limitations under the Apache License.
23 //
24 
25 
26 #include "shape_utils.h"
27 
28 #include <cassert>
29 #include <cstdio>
30 #include <cstring>
31 #include <iterator>
32 #include <fstream>
33 #include <sstream>
34 
35 //------------------------------------------------------------------------------
sgets(char * s,int size,char ** stream)36 static char const * sgets( char * s, int size, char ** stream ) {
37     for (int i=0; i<size; ++i) {
38         if ( (*stream)[i]=='\n' || (*stream)[i]=='\0') {
39 
40             memcpy(s, *stream, i);
41             s[i]='\0';
42 
43             if ((*stream)[i]=='\0')
44                 return 0;
45             else {
46                 (*stream) += i+1;
47                 return s;
48             }
49         }
50     }
51     return 0;
52 }
53 
54 //------------------------------------------------------------------------------
~Shape()55 Shape::~Shape() {
56     for (int i=0; i<(int)tags.size(); ++i)
57         delete tags[i];
58 
59     for (int i=0; i<(int)mtls.size(); ++i)
60         delete mtls[i];
61 }
62 
63 //------------------------------------------------------------------------------
parseObj(char const * shapestr,Scheme shapescheme,bool isLeftHanded,bool parsemtl)64 Shape * Shape::parseObj(char const * shapestr, Scheme shapescheme, bool isLeftHanded,
65                         bool parsemtl) {
66 
67     Shape * s = new Shape;
68 
69     s->scheme = shapescheme;
70     s->isLeftHanded = isLeftHanded;
71 
72     char * str=const_cast<char *>(shapestr), line[256], buf[256], usemtl=-1;
73     bool done = false;
74     while (! done) {
75         done = sgets(line, sizeof(line), &str)==0;
76         if (line[0]) {
77           char* end = &line[strlen(line)-1];
78           if (*end == '\n') *end = '\0'; // strip trailing nl
79         }
80         float x, y, z, u, v;
81         switch (line[0]) {
82             case 'v': switch (line[1]) {
83                           case ' ': if (sscanf(line, "v %f %f %f", &x, &y, &z) == 3) {
84                                          s->verts.push_back(x);
85                                          s->verts.push_back(y);
86                                          s->verts.push_back(z);
87                                     } break;
88                           case 't': if (sscanf(line, "vt %f %f", &u, &v) == 2) {
89                                         s->uvs.push_back(u);
90                                         s->uvs.push_back(v);
91                                     } break;
92                           case 'n' : if (sscanf(line, "vn %f %f %f", &x, &y, &z) == 3) {
93                                         s->normals.push_back(x);
94                                         s->normals.push_back(y);
95                                         s->normals.push_back(z);
96                                      } break; // skip normals for now
97                       } break;
98             case 'f': if (line[1] == ' ') {
99                           int vi, ti, ni;
100                           const char* cp = &line[2];
101                           while (*cp == ' ') cp++;
102                           int nverts = 0, nitems=0;
103                           while( (nitems=sscanf(cp, "%d/%d/%d", &vi, &ti, &ni))>0) {
104                               nverts++;
105                               s->faceverts.push_back(vi-1);
106                               if(nitems > 1) s->faceuvs.push_back(ti-1);
107                               if(nitems > 2) s->facenormals.push_back(ni-1);
108                               while (*cp && *cp != ' ') cp++;
109                               while (*cp == ' ') cp++;
110                           }
111                           s->nvertsPerFace.push_back(nverts);
112                           if (! s->mtls.empty()) {
113                               s->mtlbind.push_back(usemtl);
114                           }
115                       } break;
116             case 't' : if (line[1] == ' ') {
117                            Shape::tag * t = tag::parseTag( line );
118                            if (t)
119                                s->tags.push_back(t);
120                        } break;
121             case 'u' : if (parsemtl && sscanf(line, "usemtl %s", buf)==1) {
122                            usemtl = s->FindMaterial(buf);
123                        } break;
124             case 'm' : if (parsemtl && sscanf(line, "mtllib %s", buf)==1) {
125                            std::ifstream ifs(buf);
126                            if (ifs) {
127                                std::stringstream ss;
128                                ss << ifs.rdbuf();
129                                ifs.close();
130                                std::string tmpStr = ss.str();
131                                s->parseMtllib(tmpStr.c_str());
132                                s->mtllib = buf;
133                            }
134                        } break;
135         }
136     }
137     return s;
138 }
139 
140 //------------------------------------------------------------------------------
parseObj(ShapeDesc const & shapeDesc,bool parsemtl)141 Shape * Shape::parseObj(ShapeDesc const & shapeDesc, bool parsemtl) {
142     return parseObj(shapeDesc.data.c_str(), shapeDesc.scheme, shapeDesc.isLeftHanded,
143                     parsemtl);
144 }
145 
146 //------------------------------------------------------------------------------
parseTag(char const * line)147 Shape::tag * Shape::tag::parseTag(char const * line) {
148     tag * t = 0;
149 
150     const char* cp = &line[2];
151 
152     char tname[50];
153     while (*cp == ' ') cp++;
154     if (sscanf(cp, "%s", tname )!=1) return t;
155     while (*cp && *cp != ' ') cp++;
156 
157     int nints=0, nfloats=0, nstrings=0;
158     while (*cp == ' ') cp++;
159     if (sscanf(cp, "%d/%d/%d", &nints, &nfloats, &nstrings)!=3) return t;
160     while (*cp && *cp != ' ') cp++;
161 
162     std::vector<int> tintargs;
163     for (int i=0; i<nints; ++i) {
164         int val;
165         while (*cp == ' ') cp++;
166         if (sscanf(cp, "%d", &val)!=1) return t;
167         tintargs.push_back(val);
168         while (*cp && *cp != ' ') cp++;
169     }
170 
171     std::vector<float> tfloatargs;
172     for (int i=0; i<nfloats; ++i) {
173         float val;
174         while (*cp == ' ') cp++;
175         if (sscanf(cp, "%f", &val)!=1) return t;
176         tfloatargs.push_back(val);
177         while (*cp && *cp != ' ') cp++;
178     }
179 
180     std::vector<std::string> tstringargs;
181     for (int i=0; i<nstrings; ++i) {
182         char val[512];
183         while (*cp == ' ') cp++;
184         if (sscanf(cp, "%s", val)!=1) return t;
185         tstringargs.push_back(std::string(val));
186         while (*cp && *cp != ' ') cp++;
187     }
188 
189     t = new Shape::tag;
190     t->name = tname;
191     t->intargs = tintargs;
192     t->floatargs = tfloatargs;
193     t->stringargs = tstringargs;
194 
195     return t;
196 }
197 
198 //------------------------------------------------------------------------------
material()199 Shape::material::material() {
200     memset(ka, 0, sizeof(float)*3);
201     memset(kd, 0, sizeof(float)*3);
202     memset(ks, 0, sizeof(float)*3);
203     memset(tf, 0, sizeof(float)*3);
204     ns = ni = d = 0.0f;
205     illum=0;
206 }
207 
208 //------------------------------------------------------------------------------
parseMtllib(char const * mtlstr)209 void Shape::parseMtllib(char const * mtlstr) {
210 
211     char * str=const_cast<char *>(mtlstr), line[256];
212 
213     material * mtl=0;
214 
215     bool done = false;
216     float r, g, b, a;
217     while (! done) {
218         done = sgets(line, sizeof(line), &str)==0;
219         char* end = &line[strlen(line)-1];
220         if (*end == '\n') *end = '\0'; // strip trailing nl
221         switch (line[0]) {
222             case 'n': char name[256];
223                       if (sscanf(line, "newmtl %s", name) == 1) {
224                           mtl = new material;
225                           mtl->name = name;
226                           mtls.push_back(mtl);
227                       } break;
228             case 'K': if (sscanf(line+2, " %f %f %f", &r, &g, &b) == 3) {
229                           switch (line[1]) {
230                               case 'a': mtl->ka[0]=r; mtl->ka[1]=g; mtl->ka[2]=b; break;
231                               case 'd': mtl->kd[0]=r; mtl->kd[1]=g; mtl->kd[2]=b; break;
232                               case 's': mtl->ks[0]=r; mtl->ks[1]=g; mtl->ks[2]=b; break;
233                           }
234                       } break;
235             case 'N': if (sscanf(line+2, " %f", &a) == 1) {
236                           switch (line[1]) {
237                               case 's' : mtl->ns = a; break;
238                               case 'i' : mtl->ni = a; break;
239                           }
240                       } break;
241             case 'd': if (sscanf(line, "d %f", &a) == 1) {
242                           mtl->d = a;
243                       } break;
244             case 'T': if (line[1]=='f') {
245                          if (sscanf(line, "Tf %f %f %f", &r, &g, &b) == 3) {
246                              mtl->tf[0]=r; mtl->tf[1]=g; mtl->tf[2]=b;
247                          } break;
248                       } break;
249             case 'i': int illum;
250                       if (sscanf(line, "illum %d", &illum) == 1) {
251                           mtl->illum = illum;
252                       } break;
253             case 's': if (sscanf(line, "sharpness %f", &a) == 1) {
254                           mtl->sharpness = a;
255                       } break;
256         }
257     }
258 }
259 
260 //------------------------------------------------------------------------------
genTag() const261 std::string Shape::tag::genTag() const {
262     std::stringstream t;
263 
264     t<<"\"t \""<<name<<"\" ";
265 
266     t<<intargs.size()<<"/"<<floatargs.size()<<"/"<<stringargs.size()<<" ";
267 
268     std::copy(intargs.begin(), intargs.end(), std::ostream_iterator<int>(t));
269     t<<" ";
270 
271     std::copy(floatargs.begin(), floatargs.end(), std::ostream_iterator<float>(t));
272     t<<" ";
273 
274     std::copy(stringargs.begin(), stringargs.end(), std::ostream_iterator<std::string>(t));
275     t<<"\\n\"\n";
276 
277     return t.str();
278 }
279 
280 //------------------------------------------------------------------------------
genShape(char const * name) const281 std::string Shape::genShape(char const * name) const {
282     std::stringstream sh;
283 
284     sh<<"static char const * "<<name<<" = \n";
285 
286     for (int i=0; i<(int)verts.size(); i+=3)
287        sh << "\"v " << verts[i] << " " << verts[i+1] << " " << verts[i+2] <<"\\n\"\n";
288 
289     for (int i=0; i<(int)uvs.size(); i+=2)
290        sh << "\"vt " << uvs[i] << " " << uvs[i+1] << "\\n\"\n";
291 
292     for (int i=0; i<(int)normals.size(); i+=3)
293        sh << "\"vn " << normals[i] << " " << normals[i+1] << " " << normals[i+2] <<"\\n\"\n";
294 
295     sh << "\"s off\\n\"\n";
296 
297     for (int i=0, idx=0; i<(int)nvertsPerFace.size();++i) {
298         sh << "\"f ";
299         for (int j=0; j<nvertsPerFace[i];++j) {
300             int vert = faceverts[idx+j]+1,
301                 uv = (int)faceuvs.size()>0 ? faceuvs[idx+j]+1 : vert,
302                 normal = (int)facenormals.size()>0 ? facenormals[idx+j]+1 : vert;
303             sh << vert << "/" << uv << "/" << normal << " ";
304         }
305         sh << "\\n\"\n";
306         idx+=nvertsPerFace[i];
307     }
308 
309     for (int i=0; i<(int)tags.size(); ++i)
310         sh << tags[i]->genTag();
311 
312     return sh.str();
313 }
314 
315 //------------------------------------------------------------------------------
genObj() const316 std::string Shape::genObj() const {
317     std::stringstream sh;
318 
319     sh<<"# This file uses centimeters as units for non-parametric coordinates.\n\n";
320 
321     for (int i=0; i<(int)verts.size(); i+=3)
322        sh << "v " << verts[i] << " " << verts[i+1] << " " << verts[i+2] <<"\n";
323 
324     for (int i=0; i<(int)uvs.size(); i+=2)
325        sh << "vt " << uvs[i] << " " << uvs[i+1] << "\n";
326 
327     for (int i=0; i<(int)normals.size(); i+=3)
328        sh << "vn " << normals[i] << " " << normals[i+1] << " " << normals[i+2] <<"\n";
329 
330     for (int i=0, idx=0; i<(int)nvertsPerFace.size();++i) {
331         sh << "f ";
332         for (int j=0; j<nvertsPerFace[i];++j) {
333             int vert = faceverts[idx+j]+1,
334                 uv = (int)faceuvs.size()>0 ? faceuvs[idx+j]+1 : vert,
335                 normal = (int)facenormals.size()>0 ? facenormals[idx+j]+1 : vert;
336             sh << vert << "/" << uv << "/" << normal << " ";
337         }
338         sh << "\n";
339         idx+=nvertsPerFace[i];
340     }
341 
342     for (int i=0; i<(int)tags.size(); ++i)
343         sh << tags[i]->genTag();
344 
345     return sh.str();
346 }
347 
348 //------------------------------------------------------------------------------
genRIB() const349 std::string Shape::genRIB() const {
350     std::stringstream rib;
351 
352     rib << "HierarchicalSubdivisionMesh \"catmull-clark\" ";
353 
354     rib << "[";
355     std::copy(nvertsPerFace.begin(), nvertsPerFace.end(), std::ostream_iterator<int>(rib));
356     rib << "] ";
357 
358     rib << "[";
359     std::copy(faceverts.begin(), faceverts.end(), std::ostream_iterator<int>(rib));
360     rib << "] ";
361 
362     std::stringstream names, nargs, intargs, floatargs, strargs;
363     for (int i=0; i<(int)tags.size();) {
364         tag * t = tags[i];
365 
366         names << t->name;
367 
368         nargs << t->intargs.size() << " " << t->floatargs.size() << " " << t->stringargs.size();
369 
370         std::copy(t->intargs.begin(), t->intargs.end(), std::ostream_iterator<int>(intargs));
371 
372         std::copy(t->floatargs.begin(), t->floatargs.end(), std::ostream_iterator<float>(floatargs));
373 
374         std::copy(t->stringargs.begin(), t->stringargs.end(), std::ostream_iterator<std::string>(strargs));
375 
376         if (++i<(int)tags.size()) {
377             names << " ";
378             nargs << " ";
379             intargs << " ";
380             floatargs << " ";
381             strargs << " ";
382         }
383     }
384 
385     rib << "["<<names.str()<<"] " << "["<<nargs.str()<<"] " << "["<<intargs.str()<<"] " << "["<<floatargs.str()<<"] " << "["<<strargs.str()<<"] ";
386 
387     rib << "\"P\" [";
388     std::copy(verts.begin(), verts.end(), std::ostream_iterator<float>(rib));
389     rib << "] ";
390 
391     return rib.str();
392 }
393