1 struct obj;
2 
3 struct obj : vertmodel, vertloader<obj>
4 {
objobj5     obj(const char *name) : vertmodel(name) {}
6 
formatnameobj7     static const char *formatname() { return "obj"; }
cananimateobj8     static bool cananimate() { return false; }
typeobj9     int type() const { return MDL_OBJ; }
10 
11     struct objmeshgroup : vertmeshgroup
12     {
parsevertobj::objmeshgroup13         void parsevert(char *s, vector<vec> &out)
14         {
15             vec &v = out.add(vec(0, 0, 0));
16             while(isalpha(*s)) s++;
17             loopi(3)
18             {
19                 v[i] = strtod(s, &s);
20                 while(isspace(*s)) s++;
21                 if(!*s) break;
22             }
23         }
24 
loadobj::objmeshgroup25         bool load(const char *filename, float smooth)
26         {
27             int len = strlen(filename);
28             if(len < 4 || strcasecmp(&filename[len-4], ".obj")) return false;
29 
30             stream *file = openfile(filename, "rb");
31             if(!file) return false;
32 
33             name = newstring(filename);
34 
35             numframes = 1;
36 
37             vector<vec> attrib[3];
38             char buf[512];
39 
40             hashtable<ivec, int> verthash(1<<11);
41             vector<vert> verts;
42             vector<tcvert> tcverts;
43             vector<tri> tris;
44 
45             #define STARTMESH do { \
46                 vertmesh &m = *new vertmesh; \
47                 m.group = this; \
48                 m.name = meshname[0] ? newstring(meshname) : NULL; \
49                 meshes.add(&m); \
50                 curmesh = &m; \
51                 verthash.clear(); \
52                 verts.setsize(0); \
53                 tcverts.setsize(0); \
54                 tris.setsize(0); \
55             } while(0)
56 
57             #define FLUSHMESH do { \
58                 curmesh->numverts = verts.length(); \
59                 if(verts.length()) \
60                 { \
61                     curmesh->verts = new vert[verts.length()]; \
62                     memcpy(curmesh->verts, verts.getbuf(), verts.length()*sizeof(vert)); \
63                     curmesh->tcverts = new tcvert[verts.length()]; \
64                     memcpy(curmesh->tcverts, tcverts.getbuf(), tcverts.length()*sizeof(tcvert)); \
65                 } \
66                 curmesh->numtris = tris.length(); \
67                 if(tris.length()) \
68                 { \
69                     curmesh->tris = new tri[tris.length()]; \
70                     memcpy(curmesh->tris, tris.getbuf(), tris.length()*sizeof(tri)); \
71                 } \
72                 if(attrib[2].empty()) \
73                 { \
74                     if(smooth <= 1) curmesh->smoothnorms(smooth); \
75                     else curmesh->buildnorms(); \
76                 } \
77                 curmesh->calctangents(); \
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 
newmeshesobj163     vertmeshgroup *newmeshes() { return new objmeshgroup; }
164 
loaddefaultpartsobj165     bool loaddefaultparts()
166     {
167         part &mdl = addpart();
168         const char *pname = parentdir(name);
169         defformatstring(name1, "media/model/%s/tris.obj", name);
170         mdl.meshes = sharemeshes(path(name1));
171         if(!mdl.meshes)
172         {
173             defformatstring(name2, "media/model/%s/tris.obj", pname);    // try obj in parent folder (vert sharing)
174             mdl.meshes = sharemeshes(path(name2));
175             if(!mdl.meshes) return false;
176         }
177         Texture *tex, *masks;
178         loadskin(name, pname, tex, masks);
179         mdl.initskins(tex, masks);
180         if(tex==notexture) conoutf("could not load model skin for %s", name1);
181         return true;
182     }
183 
loadobj184     bool load()
185     {
186         formatstring(dir, "media/model/%s", name);
187         defformatstring(cfgname, "media/model/%s/obj.cfg", name);
188 
189         loading = this;
190         identflags &= ~IDF_PERSIST;
191         if(execfile(cfgname, false) && parts.length()) // configured obj, will call the obj* commands below
192         {
193             identflags |= IDF_PERSIST;
194             loading = NULL;
195             loopv(parts) if(!parts[i]->meshes) return false;
196         }
197         else // obj without configuration, try default tris and skin
198         {
199             identflags |= IDF_PERSIST;
200             loading = NULL;
201             if(!loaddefaultparts()) return false;
202         }
203         translate.y = -translate.y;
204         loaded();
205         return true;
206     }
207 };
208 
209 vertcommands<obj> objcommands;
210 
211