diff options
Diffstat (limited to 'src/engine/smd.h')
| -rw-r--r-- | src/engine/smd.h | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/src/engine/smd.h b/src/engine/smd.h new file mode 100644 index 0000000..771ec9a --- /dev/null +++ b/src/engine/smd.h @@ -0,0 +1,447 @@ +struct smd; + +struct smdbone +{ + string name; + int parent; + smdbone() : parent(-1) { name[0] = '\0'; } +}; + +struct smd : skelloader<smd> +{ + smd(const char *name) : skelloader(name) {} + + static const char *formatname() { return "smd"; } + int type() const { return MDL_SMD; } + + struct smdmesh : skelmesh + { + }; + + struct smdmeshgroup : skelmeshgroup + { + smdmeshgroup() + { + } + + bool skipcomment(char *&curbuf) + { + while(*curbuf && isspace(*curbuf)) curbuf++; + switch(*curbuf) + { + case '#': + case ';': + case '\r': + case '\n': + case '\0': + return true; + case '/': + if(curbuf[1] == '/') return true; + break; + } + return false; + } + + void skipsection(stream *f, char *buf, size_t bufsize) + { + while(f->getline(buf, bufsize)) + { + char *curbuf = buf; + if(skipcomment(curbuf)) continue; + if(!strncmp(curbuf, "end", 3)) break; + } + } + + void readname(char *&curbuf, char *name, size_t namesize) + { + char *curname = name; + while(*curbuf && isspace(*curbuf)) curbuf++; + bool allowspace = false; + if(*curbuf == '"') { curbuf++; allowspace = true; } + while(*curbuf) + { + char c = *curbuf++; + if(c == '"') break; + if(isspace(c) && !allowspace) break; + if(curname < &name[namesize-1]) *curname++ = c; + } + *curname = '\0'; + } + + void readnodes(stream *f, char *buf, size_t bufsize, vector<smdbone> &bones) + { + while(f->getline(buf, bufsize)) + { + char *curbuf = buf; + if(skipcomment(curbuf)) continue; + if(!strncmp(curbuf, "end", 3)) break; + int id = strtol(curbuf, &curbuf, 10); + string name; + readname(curbuf, name, sizeof(name)); + int parent = strtol(curbuf, &curbuf, 10); + if(id < 0 || id > 255 || parent > 255 || !name[0]) continue; + while(!bones.inrange(id)) bones.add(); + smdbone &bone = bones[id]; + copystring(bone.name, name); + bone.parent = parent; + } + } + + void readmaterial(char *&curbuf, char *name, size_t namesize) + { + char *curname = name; + while(*curbuf && isspace(*curbuf)) curbuf++; + while(*curbuf) + { + char c = *curbuf++; + if(isspace(c)) break; + if(c == '.') + { + while(*curbuf && !isspace(*curbuf)) curbuf++; + break; + } + if(curname < &name[namesize-1]) *curname++ = c; + } + *curname = '\0'; + } + + struct smdmeshdata + { + smdmesh *mesh; + vector<vert> verts; + vector<tri> tris; + + void finalize() + { + if(verts.empty() || tris.empty()) return; + vert *mverts = new vert[mesh->numverts + verts.length()]; + if(mesh->numverts) + { + memcpy(mverts, mesh->verts, mesh->numverts*sizeof(vert)); + delete[] mesh->verts; + } + memcpy(&mverts[mesh->numverts], verts.getbuf(), verts.length()*sizeof(vert)); + mesh->numverts += verts.length(); + mesh->verts = mverts; + tri *mtris = new tri[mesh->numtris + tris.length()]; + if(mesh->numtris) + { + memcpy(mtris, mesh->tris, mesh->numtris*sizeof(tri)); + delete[] mesh->tris; + } + memcpy(&mtris[mesh->numtris], tris.getbuf(), tris.length()*sizeof(tri)); + mesh->numtris += tris.length(); + mesh->tris = mtris; + } + }; + + struct smdvertkey : vert + { + smdmeshdata *mesh; + + smdvertkey(smdmeshdata *mesh) : mesh(mesh) {} + }; + + void readtriangles(stream *f, char *buf, size_t bufsize) + { + smdmeshdata *curmesh = NULL; + hashtable<const char *, smdmeshdata> materials(1<<6); + hashset<int> verts(1<<12); + while(f->getline(buf, bufsize)) + { + char *curbuf = buf; + if(skipcomment(curbuf)) continue; + if(!strncmp(curbuf, "end", 3)) break; + string material; + readmaterial(curbuf, material, sizeof(material)); + if(!curmesh || strcmp(curmesh->mesh->name, material)) + { + curmesh = materials.access(material); + if(!curmesh) + { + smdmesh *m = new smdmesh; + m->group = this; + m->name = newstring(material); + meshes.add(m); + curmesh = &materials[m->name]; + curmesh->mesh = m; + } + } + tri curtri; + loopi(3) + { + char *curbuf; + do + { + if(!f->getline(buf, bufsize)) goto endsection; + curbuf = buf; + } while(skipcomment(curbuf)); + smdvertkey key(curmesh); + int parent = -1, numlinks = 0, len = 0; + if(sscanf(curbuf, " %d %f %f %f %f %f %f %f %f %d%n", &parent, &key.pos.x, &key.pos.y, &key.pos.z, &key.norm.x, &key.norm.y, &key.norm.z, &key.tc.x, &key.tc.y, &numlinks, &len) < 9) goto endsection; + curbuf += len; + key.pos.y = -key.pos.y; + key.norm.y = -key.norm.y; + key.tc.y = 1 - key.tc.y; + blendcombo c; + int sorted = 0; + float pweight = 0, tweight = 0; + for(; numlinks > 0; numlinks--) + { + int bone = -1, len = 0; + float weight = 0; + if(sscanf(curbuf, " %d %f%n", &bone, &weight, &len) < 2) break; + curbuf += len; + tweight += weight; + if(bone == parent) pweight += weight; + else sorted = c.addweight(sorted, weight, bone); + } + if(tweight < 1) pweight += 1 - tweight; + if(pweight > 0) sorted = c.addweight(sorted, pweight, parent); + c.finalize(sorted); + key.blend = curmesh->mesh->addblendcombo(c); + int index = verts.access(key, curmesh->verts.length()); + if(index == curmesh->verts.length()) curmesh->verts.add(key); + curtri.vert[2-i] = index; + } + curmesh->tris.add(curtri); + } + endsection: + enumerate(materials, smdmeshdata, data, data.finalize()); + } + + void readskeleton(stream *f, char *buf, size_t bufsize) + { + int frame = -1; + while(f->getline(buf, bufsize)) + { + char *curbuf = buf; + if(skipcomment(curbuf)) continue; + if(sscanf(curbuf, " time %d", &frame) == 1) continue; + else if(!strncmp(curbuf, "end", 3)) break; + else if(frame != 0) continue; + int bone; + vec pos, rot; + if(sscanf(curbuf, " %d %f %f %f %f %f %f", &bone, &pos.x, &pos.y, &pos.z, &rot.x, &rot.y, &rot.z) != 7) + continue; + if(bone < 0 || bone >= skel->numbones) + continue; + rot.x = -rot.x; + rot.z = -rot.z; + float cx = cosf(rot.x/2), sx = sinf(rot.x/2), + cy = cosf(rot.y/2), sy = sinf(rot.y/2), + cz = cosf(rot.z/2), sz = sinf(rot.z/2); + pos.y = -pos.y; + dualquat dq(quat(sx*cy*cz - cx*sy*sz, + cx*sy*cz + sx*cy*sz, + cx*cy*sz - sx*sy*cz, + cx*cy*cz + sx*sy*sz), + pos); + boneinfo &b = skel->bones[bone]; + if(b.parent < 0) b.base = dq; + else b.base.mul(skel->bones[b.parent].base, dq); + (b.invbase = b.base).invert(); + } + } + + bool loadmesh(const char *filename) + { + stream *f = openfile(filename, "r"); + if(!f) return false; + + char buf[512]; + int version = -1; + while(f->getline(buf, sizeof(buf))) + { + char *curbuf = buf; + if(skipcomment(curbuf)) continue; + if(sscanf(curbuf, " version %d", &version) == 1) + { + if(version != 1) { delete f; return false; } + } + else if(!strncmp(curbuf, "nodes", 5)) + { + if(skel->numbones > 0) { skipsection(f, buf, sizeof(buf)); continue; } + vector<smdbone> bones; + readnodes(f, buf, sizeof(buf), bones); + if(bones.empty()) continue; + skel->numbones = bones.length(); + skel->bones = new boneinfo[skel->numbones]; + loopv(bones) + { + boneinfo &dst = skel->bones[i]; + smdbone &src = bones[i]; + dst.name = newstring(src.name); + dst.parent = src.parent; + } + skel->linkchildren(); + } + else if(!strncmp(curbuf, "triangles", 9)) + readtriangles(f, buf, sizeof(buf)); + else if(!strncmp(curbuf, "skeleton", 8)) + { + if(skel->shared > 1) skipsection(f, buf, sizeof(buf)); + else readskeleton(f, buf, sizeof(buf)); + } + else if(!strncmp(curbuf, "vertexanimation", 15)) + skipsection(f, buf, sizeof(buf)); + } + + sortblendcombos(); + + delete f; + return true; + } + + int readframes(stream *f, char *buf, size_t bufsize, vector<dualquat> &animbones) + { + int frame = -1, numframes = 0, lastbone = skel->numbones; + while(f->getline(buf, bufsize)) + { + char *curbuf = buf; + if(skipcomment(curbuf)) continue; + int nextframe = -1; + if(sscanf(curbuf, " time %d", &nextframe) == 1) + { + for(; lastbone < skel->numbones; lastbone++) animbones[frame*skel->numbones + lastbone] = animbones[lastbone]; + if(nextframe >= numframes) + { + databuf<dualquat> framebones = animbones.reserve(skel->numbones * (nextframe + 1 - numframes)); + loopi(nextframe - numframes) framebones.put(animbones.getbuf(), skel->numbones); + animbones.addbuf(framebones); + animbones.advance(skel->numbones); + numframes = nextframe + 1; + } + frame = nextframe; + lastbone = 0; + continue; + } + else if(!strncmp(curbuf, "end", 3)) break; + int bone; + vec pos, rot; + if(sscanf(curbuf, " %d %f %f %f %f %f %f", &bone, &pos.x, &pos.y, &pos.z, &rot.x, &rot.y, &rot.z) != 7) + continue; + if(bone < 0 || bone >= skel->numbones) + continue; + for(; lastbone < bone; lastbone++) animbones[frame*skel->numbones + lastbone] = animbones[lastbone]; + lastbone++; + float cx = cosf(rot.x/2), sx = sinf(rot.x/2), + cy = cosf(rot.y/2), sy = sinf(rot.y/2), + cz = cosf(rot.z/2), sz = sinf(rot.z/2); + pos.y = -pos.y; + dualquat dq(quat(-(sx*cy*cz - cx*sy*sz), + cx*sy*cz + sx*cy*sz, + -(cx*cy*sz - sx*sy*cz), + cx*cy*cz + sx*sy*sz), + pos); + if(adjustments.inrange(bone)) adjustments[bone].adjust(dq); + dq.mul(skel->bones[bone].invbase); + dualquat &dst = animbones[frame*skel->numbones + bone]; + if(skel->bones[bone].parent < 0) dst = dq; + else dst.mul(skel->bones[skel->bones[bone].parent].base, dq); + dst.fixantipodal(skel->numframes > 0 ? skel->framebones[bone] : animbones[bone]); + } + for(; lastbone < skel->numbones; lastbone++) animbones[frame*skel->numbones + lastbone] = animbones[lastbone]; + return numframes; + } + + skelanimspec *loadanim(const char *filename) + { + skelanimspec *sa = skel->findskelanim(filename); + if(sa || skel->numbones <= 0) return sa; + + stream *f = openfile(filename, "r"); + if(!f) return NULL; + + char buf[512]; + int version = -1; + vector<dualquat> animbones; + while(f->getline(buf, sizeof(buf))) + { + char *curbuf = buf; + if(skipcomment(curbuf)) continue; + if(sscanf(curbuf, " version %d", &version) == 1) + { + if(version != 1) { delete f; return NULL; } + } + else if(!strncmp(curbuf, "nodes", 5)) + { + vector<smdbone> bones; + readnodes(f, buf, sizeof(buf), bones); + if(bones.length() != skel->numbones) { delete f; return NULL; } + } + else if(!strncmp(curbuf, "triangles", 9)) + skipsection(f, buf, sizeof(buf)); + else if(!strncmp(curbuf, "skeleton", 8)) + readframes(f, buf, sizeof(buf), animbones); + else if(!strncmp(curbuf, "vertexanimation", 15)) + skipsection(f, buf, sizeof(buf)); + } + int numframes = animbones.length() / skel->numbones; + dualquat *framebones = new dualquat[(skel->numframes+numframes)*skel->numbones]; + if(skel->framebones) + { + memcpy(framebones, skel->framebones, skel->numframes*skel->numbones*sizeof(dualquat)); + delete[] skel->framebones; + } + memcpy(&framebones[skel->numframes*skel->numbones], animbones.getbuf(), numframes*skel->numbones*sizeof(dualquat)); + skel->framebones = framebones; + sa = &skel->addskelanim(filename); + sa->frame = skel->numframes; + sa->range = numframes; + skel->numframes += numframes; + + delete f; + + return sa; + } + + bool load(const char *meshfile) + { + name = newstring(meshfile); + + if(!loadmesh(meshfile)) return false; + + return true; + } + }; + + meshgroup *loadmeshes(const char *name, va_list args) + { + smdmeshgroup *group = new smdmeshgroup; + group->shareskeleton(va_arg(args, char *)); + if(!group->load(name)) { 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.smd", name, fname); + mdl.meshes = sharemeshes(path(meshname), NULL); + if(!mdl.meshes) return false; + mdl.initanimparts(); + mdl.initskins(); + return true; + } +}; + +static inline uint hthash(const smd::smdmeshgroup::smdvertkey &k) +{ + return hthash(k.pos); +} + +static inline bool htcmp(const smd::smdmeshgroup::smdvertkey &k, int index) +{ + if(!k.mesh->verts.inrange(index)) return false; + const smd::vert &v = k.mesh->verts[index]; + return k.pos == v.pos && k.norm == v.norm && k.tc == v.tc && k.blend == v.blend; +} + +skelcommands<smd> smdcommands; + |
