diff options
Diffstat (limited to 'src/engine/md5.h')
| -rw-r--r-- | src/engine/md5.h | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/src/engine/md5.h b/src/engine/md5.h new file mode 100644 index 0000000..0d59586 --- /dev/null +++ b/src/engine/md5.h @@ -0,0 +1,419 @@ +struct md5; + +struct md5joint +{ + vec pos; + quat orient; +}; + +struct md5weight +{ + int joint; + float bias; + vec pos; +}; + +struct md5vert +{ + vec2 tc; + ushort start, count; +}; + +struct md5hierarchy +{ + string name; + int parent, flags, start; +}; + +struct md5 : skelloader<md5> +{ + md5(const char *name) : skelloader(name) {} + + static const char *formatname() { return "md5"; } + int type() const { return MDL_MD5; } + + struct md5mesh : skelmesh + { + md5weight *weightinfo; + int numweights; + md5vert *vertinfo; + + md5mesh() : weightinfo(NULL), numweights(0), vertinfo(NULL) + { + } + + ~md5mesh() + { + cleanup(); + } + + void cleanup() + { + DELETEA(weightinfo); + DELETEA(vertinfo); + } + + void buildverts(vector<md5joint> &joints) + { + loopi(numverts) + { + md5vert &v = vertinfo[i]; + vec pos(0, 0, 0); + loopk(v.count) + { + md5weight &w = weightinfo[v.start+k]; + md5joint &j = joints[w.joint]; + vec wpos = j.orient.rotate(w.pos); + wpos.add(j.pos); + wpos.mul(w.bias); + pos.add(wpos); + } + vert &vv = verts[i]; + vv.pos = pos; + vv.tc = v.tc; + + blendcombo c; + int sorted = 0; + loopj(v.count) + { + md5weight &w = weightinfo[v.start+j]; + sorted = c.addweight(sorted, w.bias, w.joint); + } + c.finalize(sorted); + vv.blend = addblendcombo(c); + } + } + + void load(stream *f, char *buf, size_t bufsize) + { + md5weight w; + md5vert v; + tri t; + int index; + + while(f->getline(buf, bufsize) && buf[0]!='}') + { + if(strstr(buf, "// meshes:")) + { + char *start = strchr(buf, ':')+1; + if(*start==' ') start++; + char *end = start + strlen(start)-1; + while(end >= start && isspace(*end)) end--; + name = newstring(start, end+1-start); + } + else if(strstr(buf, "shader")) + { + char *start = strchr(buf, '"'), *end = start ? strchr(start+1, '"') : NULL; + if(start && end) + { + char *texname = newstring(start+1, end-(start+1)); + part *p = loading->parts.last(); + p->initskins(notexture, notexture, group->meshes.length()); + skin &s = p->skins.last(); + s.tex = textureload(makerelpath(dir, texname), 0, true, false); + delete[] texname; + } + } + else if(sscanf(buf, " numverts %d", &numverts)==1) + { + numverts = max(numverts, 0); + if(numverts) + { + vertinfo = new md5vert[numverts]; + verts = new vert[numverts]; + } + } + else if(sscanf(buf, " numtris %d", &numtris)==1) + { + numtris = max(numtris, 0); + if(numtris) tris = new tri[numtris]; + } + else if(sscanf(buf, " numweights %d", &numweights)==1) + { + numweights = max(numweights, 0); + if(numweights) weightinfo = new md5weight[numweights]; + } + else if(sscanf(buf, " vert %d ( %f %f ) %hu %hu", &index, &v.tc.x, &v.tc.y, &v.start, &v.count)==5) + { + if(index>=0 && index<numverts) vertinfo[index] = v; + } + else if(sscanf(buf, " tri %d %hu %hu %hu", &index, &t.vert[0], &t.vert[1], &t.vert[2])==4) + { + if(index>=0 && index<numtris) tris[index] = t; + } + else if(sscanf(buf, " weight %d %d %f ( %f %f %f ) ", &index, &w.joint, &w.bias, &w.pos.x, &w.pos.y, &w.pos.z)==6) + { + w.pos.y = -w.pos.y; + if(index>=0 && index<numweights) weightinfo[index] = w; + } + } + } + }; + + struct md5meshgroup : skelmeshgroup + { + md5meshgroup() + { + } + + bool loadmesh(const char *filename, float smooth) + { + stream *f = openfile(filename, "r"); + if(!f) return false; + + char buf[512]; + vector<md5joint> basejoints; + while(f->getline(buf, sizeof(buf))) + { + int tmp; + if(sscanf(buf, " MD5Version %d", &tmp)==1) + { + if(tmp!=10) { delete f; return false; } + } + else if(sscanf(buf, " numJoints %d", &tmp)==1) + { + if(tmp<1) { delete f; return false; } + if(skel->numbones>0) continue; + skel->numbones = tmp; + skel->bones = new boneinfo[skel->numbones]; + } + else if(sscanf(buf, " numMeshes %d", &tmp)==1) + { + if(tmp<1) { delete f; return false; } + } + else if(strstr(buf, "joints {")) + { + string name; + int parent; + md5joint j; + while(f->getline(buf, sizeof(buf)) && buf[0]!='}') + { + char *curbuf = buf, *curname = name; + bool allowspace = false; + while(*curbuf && isspace(*curbuf)) curbuf++; + if(*curbuf == '"') { curbuf++; allowspace = true; } + while(*curbuf && curname < &name[sizeof(name)-1]) + { + char c = *curbuf++; + if(c == '"') break; + if(isspace(c) && !allowspace) break; + *curname++ = c; + } + *curname = '\0'; + if(sscanf(curbuf, " %d ( %f %f %f ) ( %f %f %f )", + &parent, &j.pos.x, &j.pos.y, &j.pos.z, + &j.orient.x, &j.orient.y, &j.orient.z)==7) + { + j.pos.y = -j.pos.y; + j.orient.x = -j.orient.x; + j.orient.z = -j.orient.z; + if(basejoints.length()<skel->numbones) + { + if(!skel->bones[basejoints.length()].name) + skel->bones[basejoints.length()].name = newstring(name); + skel->bones[basejoints.length()].parent = parent; + } + j.orient.restorew(); + basejoints.add(j); + } + } + if(basejoints.length()!=skel->numbones) { delete f; return false; } + } + else if(strstr(buf, "mesh {")) + { + md5mesh *m = new md5mesh; + m->group = this; + meshes.add(m); + m->load(f, buf, sizeof(buf)); + if(!m->numtris || !m->numverts) + { + conoutf(CON_WARN, "empty mesh in %s", filename); + meshes.removeobj(m); + delete m; + } + } + } + + if(skel->shared <= 1) + { + skel->linkchildren(); + loopv(basejoints) + { + boneinfo &b = skel->bones[i]; + b.base = dualquat(basejoints[i].orient, basejoints[i].pos); + (b.invbase = b.base).invert(); + } + } + + loopv(meshes) + { + md5mesh &m = *(md5mesh *)meshes[i]; + m.buildverts(basejoints); + if(smooth <= 1) m.smoothnorms(smooth); + else m.buildnorms(); + m.cleanup(); + } + + sortblendcombos(); + + delete f; + return true; + } + + skelanimspec *loadanim(const char *filename) + { + skelanimspec *sa = skel->findskelanim(filename); + if(sa) return sa; + + stream *f = openfile(filename, "r"); + if(!f) return NULL; + + vector<md5hierarchy> hierarchy; + vector<md5joint> basejoints; + int animdatalen = 0, animframes = 0; + float *animdata = NULL; + dualquat *animbones = NULL; + char buf[512]; + while(f->getline(buf, sizeof(buf))) + { + int tmp; + if(sscanf(buf, " MD5Version %d", &tmp)==1) + { + if(tmp!=10) { delete f; return NULL; } + } + else if(sscanf(buf, " numJoints %d", &tmp)==1) + { + if(tmp!=skel->numbones) { delete f; return NULL; } + } + else if(sscanf(buf, " numFrames %d", &animframes)==1) + { + if(animframes<1) { delete f; return NULL; } + } + else if(sscanf(buf, " frameRate %d", &tmp)==1); + else if(sscanf(buf, " numAnimatedComponents %d", &animdatalen)==1) + { + if(animdatalen>0) animdata = new float[animdatalen]; + } + else if(strstr(buf, "bounds {")) + { + while(f->getline(buf, sizeof(buf)) && buf[0]!='}'); + } + else if(strstr(buf, "hierarchy {")) + { + while(f->getline(buf, sizeof(buf)) && buf[0]!='}') + { + md5hierarchy h; + if(sscanf(buf, " %100s %d %d %d", h.name, &h.parent, &h.flags, &h.start)==4) + hierarchy.add(h); + } + } + else if(strstr(buf, "baseframe {")) + { + while(f->getline(buf, sizeof(buf)) && buf[0]!='}') + { + md5joint j; + if(sscanf(buf, " ( %f %f %f ) ( %f %f %f )", &j.pos.x, &j.pos.y, &j.pos.z, &j.orient.x, &j.orient.y, &j.orient.z)==6) + { + j.pos.y = -j.pos.y; + j.orient.x = -j.orient.x; + j.orient.z = -j.orient.z; + j.orient.restorew(); + basejoints.add(j); + } + } + if(basejoints.length()!=skel->numbones) { delete f; if(animdata) delete[] animdata; return NULL; } + animbones = new dualquat[(skel->numframes+animframes)*skel->numbones]; + if(skel->framebones) + { + memcpy(animbones, skel->framebones, skel->numframes*skel->numbones*sizeof(dualquat)); + delete[] skel->framebones; + } + skel->framebones = animbones; + animbones += skel->numframes*skel->numbones; + + sa = &skel->addskelanim(filename); + sa->frame = skel->numframes; + sa->range = animframes; + + skel->numframes += animframes; + } + else if(sscanf(buf, " frame %d", &tmp)==1) + { + for(int numdata = 0; f->getline(buf, sizeof(buf)) && buf[0]!='}';) + { + for(char *src = buf, *next = src; numdata < animdatalen; numdata++, src = next) + { + animdata[numdata] = strtod(src, &next); + if(next <= src) break; + } + } + dualquat *frame = &animbones[tmp*skel->numbones]; + loopv(basejoints) + { + md5hierarchy &h = hierarchy[i]; + md5joint j = basejoints[i]; + if(h.start < animdatalen && h.flags) + { + float *jdata = &animdata[h.start]; + if(h.flags&1) j.pos.x = *jdata++; + if(h.flags&2) j.pos.y = -*jdata++; + if(h.flags&4) j.pos.z = *jdata++; + if(h.flags&8) j.orient.x = -*jdata++; + if(h.flags&16) j.orient.y = *jdata++; + if(h.flags&32) j.orient.z = -*jdata++; + j.orient.restorew(); + } + frame[i] = dualquat(j.orient, j.pos); + if(adjustments.inrange(i)) adjustments[i].adjust(frame[i]); + frame[i].mul(skel->bones[i].invbase); + if(h.parent >= 0) frame[i].mul(skel->bones[h.parent].base, dualquat(frame[i])); + frame[i].fixantipodal(skel->framebones[i]); + } + } + } + + if(animdata) delete[] animdata; + delete f; + + return sa; + } + + bool load(const char *meshfile, float smooth) + { + name = newstring(meshfile); + + if(!loadmesh(meshfile, smooth)) return false; + + return true; + } + }; + + meshgroup *loadmeshes(const char *name, va_list args) + { + md5meshgroup *group = new md5meshgroup; + group->shareskeleton(va_arg(args, char *)); + if(!group->load(name, va_arg(args, double))) { delete group; return NULL; } + return group; + } + + bool loaddefaultparts() + { + skelpart &mdl = addpart(); + mdl.pitchscale = mdl.pitchoffset = mdl.pitchmin = mdl.pitchmax = 0; + adjustments.setsize(0); + const char *fname = name + strlen(name); + do --fname; while(fname >= name && *fname!='/' && *fname!='\\'); + fname++; + defformatstring(meshname, "packages/models/%s/%s.md5mesh", name, fname); + mdl.meshes = sharemeshes(path(meshname), NULL, 2.0); + if(!mdl.meshes) return false; + mdl.initanimparts(); + mdl.initskins(); + defformatstring(animname, "packages/models/%s/%s.md5anim", name, fname); + ((md5meshgroup *)mdl.meshes)->loadanim(path(animname)); + return true; + } +}; + +skelcommands<md5> md5commands; + |
