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