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