1 struct obj;
2 
3 struct obj : vertloader<obj>
4 {
objobj5     obj(const char *name) : vertloader(name) {}
6 
formatnameobj7     static const char *formatname() { return "obj"; }
animatedobj8     static bool animated() { return false; }
flipyobj9     bool flipy() const { return true; }
typeobj10     int type() const { return MDL_OBJ; }
11 
12     struct objmeshgroup : vertmeshgroup
13     {
parsevertobj::objmeshgroup14         void parsevert(char *s, vector<vec> &out)
15         {
16             vec &v = out.add(vec(0, 0, 0));
17             while(isalpha(*s)) s++;
18             loopi(3)
19             {
20                 v[i] = strtod(s, &s);
21                 while(isspace(*s)) s++;
22                 if(!*s) break;
23             }
24         }
25 
loadobj::objmeshgroup26         bool load(const char *filename, float smooth)
27         {
28             int len = strlen(filename);
29             if(len < 4 || strcasecmp(&filename[len-4], ".obj")) return false;
30 
31             stream *file = openfile(filename, "rb");
32             if(!file) return false;
33 
34             name = newstring(filename);
35 
36             numframes = 1;
37 
38             vector<vec> attrib[3];
39             char buf[512];
40 
41             hashtable<ivec, int> verthash;
42             vector<vert> verts;
43             vector<tcvert> tcverts;
44             vector<tri> tris;
45 
46             #define STARTMESH do { \
47                 vertmesh &m = *new vertmesh; \
48                 m.group = this; \
49                 m.name = meshname[0] ? newstring(meshname) : NULL; \
50                 meshes.add(&m); \
51                 curmesh = &m; \
52                 verthash.clear(); \
53                 verts.setsize(0); \
54                 tcverts.setsize(0); \
55                 tris.setsize(0); \
56             } while(0)
57 
58             #define FLUSHMESH do { \
59                 curmesh->numverts = verts.length(); \
60                 if(verts.length()) \
61                 { \
62                     curmesh->verts = new vert[verts.length()]; \
63                     memcpy(curmesh->verts, verts.getbuf(), verts.length()*sizeof(vert)); \
64                     curmesh->tcverts = new tcvert[verts.length()]; \
65                     memcpy(curmesh->tcverts, tcverts.getbuf(), tcverts.length()*sizeof(tcvert)); \
66                 } \
67                 curmesh->numtris = tris.length(); \
68                 if(tris.length()) \
69                 { \
70                     curmesh->tris = new tri[tris.length()]; \
71                     memcpy(curmesh->tris, tris.getbuf(), tris.length()*sizeof(tri)); \
72                 } \
73                 if(attrib[2].empty()) \
74                 { \
75                     if(smooth <= 1) curmesh->smoothnorms(smooth); \
76                     else curmesh->buildnorms(); \
77                 } \
78             } while(0)
79 
80             string meshname = "";
81             vertmesh *curmesh = NULL;
82             while(file->getline(buf, sizeof(buf)))
83             {
84                 char *c = buf;
85                 while(isspace(*c)) c++;
86                 switch(*c)
87                 {
88                     case '#': continue;
89                     case 'v':
90                         if(isspace(c[1])) parsevert(c, attrib[0]);
91                         else if(c[1]=='t') parsevert(c, attrib[1]);
92                         else if(c[1]=='n') parsevert(c, attrib[2]);
93                         break;
94                     case 'g':
95                     {
96                         while(isalpha(*c)) c++;
97                         while(isspace(*c)) c++;
98                         char *name = c;
99                         size_t namelen = strlen(name);
100                         while(namelen > 0 && isspace(name[namelen-1])) namelen--;
101                         copystring(meshname, name, min(namelen+1, sizeof(meshname)));
102 
103                         if(curmesh) FLUSHMESH;
104                         curmesh = NULL;
105                         break;
106                     }
107                     case 'f':
108                     {
109                         if(!curmesh) STARTMESH;
110                         int v0 = -1, v1 = -1;
111                         while(isalpha(*c)) c++;
112                         for(;;)
113                         {
114                             while(isspace(*c)) c++;
115                             if(!*c) break;
116                             ivec vkey(-1, -1, -1);
117                             loopi(3)
118                             {
119                                 vkey[i] = strtol(c, &c, 10);
120                                 if(vkey[i] < 0) vkey[i] = attrib[i].length() + vkey[i];
121                                 else vkey[i]--;
122                                 if(!attrib[i].inrange(vkey[i])) vkey[i] = -1;
123                                 if(*c!='/') break;
124                                 c++;
125                             }
126                             int *index = verthash.access(vkey);
127                             if(!index)
128                             {
129                                 index = &verthash[vkey];
130                                 *index = verts.length();
131                                 vert &v = verts.add();
132                                 v.pos = vkey.x < 0 ? vec(0, 0, 0) : attrib[0][vkey.x];
133                                 v.pos = vec(v.pos.z, -v.pos.x, v.pos.y);
134                                 v.norm = vkey.z < 0 ? vec(0, 0, 0) : attrib[2][vkey.z];
135                                 v.norm = vec(v.norm.z, -v.norm.x, v.norm.y);
136                                 tcvert &tcv = tcverts.add();
137                                 tcv.tc = vkey.y < 0 ? vec2(0, 0) : vec2(attrib[1][vkey.y].x, 1-attrib[1][vkey.y].y);
138                             }
139                             if(v0 < 0) v0 = *index;
140                             else if(v1 < 0) v1 = *index;
141                             else
142                             {
143                                 tri &t = tris.add();
144                                 t.vert[0] = ushort(*index);
145                                 t.vert[1] = ushort(v1);
146                                 t.vert[2] = ushort(v0);
147                                 v1 = *index;
148                             }
149                         }
150                         break;
151                     }
152                 }
153             }
154 
155             if(curmesh) FLUSHMESH;
156 
157             delete file;
158 
159             return true;
160         }
161     };
162 
loadmeshesobj163     meshgroup *loadmeshes(const char *name, va_list args)
164     {
165         objmeshgroup *group = new objmeshgroup;
166         if(!group->load(name, va_arg(args, double))) { delete group; return NULL; }
167         return group;
168     }
169 
loaddefaultpartsobj170     bool loaddefaultparts()
171     {
172         part &mdl = addpart();
173         const char *pname = parentdir(name);
174         defformatstring(name1, "packages/models/%s/tris.obj", name);
175         mdl.meshes = sharemeshes(path(name1), 2.0);
176         if(!mdl.meshes)
177         {
178             defformatstring(name2, "packages/models/%s/tris.obj", pname);    // try obj in parent folder (vert sharing)
179             mdl.meshes = sharemeshes(path(name2), 2.0);
180             if(!mdl.meshes) return false;
181         }
182         Texture *tex, *masks;
183         loadskin(name, pname, tex, masks);
184         mdl.initskins(tex, masks);
185         if(tex==notexture) conoutf(CON_ERROR, "could not load model skin for %s", name1);
186         return true;
187     }
188 };
189 
190 vertcommands<obj> objcommands;
191 
192