#include "md2model.h" #include "md2norms.h" #include "std.h" static Vector* normals; static float white[] = {1, 1, 1}; extern gxRuntime* gx_runtime; extern gxGraphics* gx_graphics; struct MD2Model::Rep { struct md2_header { int magic; int version; int skinWidth; int skinHeight; int frameSize; int numSkins; int numVertices; int numTexCoords; int numTriangles; int numGlCommands; int numFrames; int offsetSkins; int offsetTexCoords; int offsetTriangles; int offsetFrames; int offsetGlCommands; int offsetEnd; }; struct md2_vertex { unsigned char x, y, z, n; }; struct md2_texcoord { unsigned short s, t; }; struct md2_triangle { unsigned short verts[3], tex_coords[3]; }; struct Frame { Vector scale, trans; vector verts; }; struct TexCoords { float u, v; }; int ref_cnt; int num_verts, num_frames; vector frames; vector tex_coords; gxMesh* mesh; Box box; ModelModel::Rep(const string& file) : ref_cnt(1), mesh(0) { filebuf in; Header header; if (!in.open(file.c_str(), ios_base::in | ios_base::binary)) return; if (in.sgetn((char*)&header, sizeof(header)) != sizeof(header)) return; if (header.magic != '2PDI' || header.version != 8) return; //read tex coords in.pubseekpos(header.offsetTexCoords); TexCoord* coords = d_new TexCoord[header.numTexCoords]; in.sgetn((char*)coords, header.numTexCoords * sizeof(TexCoord)); vector verts; map info_map; //build triangles vector triangles; in.pubseekpos(header.offsetTriangles); triangles.resize(header.numTriangles); for (k = 0; k < triangles.size(); ++k) { unsigned short v[3], t[3]; in.sgetn((char*)v, 6); in.sgetn((char*)t, 6); for (int j = 0; j < 3; ++j) { unsigned char tu = coords[t[j]].s * 256.0f / header.skinWidth; unsigned char tv = coords[t[j]].t * 256.0f / header.skinHeight; VertInfo i(v[j], tu, tv); map::iterator it = info_map.find(i); if (it == info_map.end()) { info_map[i] = triangles[k].v[j] = verts.size(); verts.push_back(i); } else { triangles[k].v[j] = it->second; } } } delete coords; //load frames string tt = "MD2 Frames:" + itoa(header.numFrames); gx_runtime->debugLog(tt.c_str()); in.pubseekpos(header.offsetFrames); frames.resize(header.numFrames); MD2Vertex* md2_verts = d_new MD2Vertex[header.numVertices]; for (k = 0; k < frames.size(); ++k) { Frame& frame = frames[k]; //read frame header; in.sgetn((char*)&frame.scale, 12); in.sgetn((char*)&frame.trans, 12); in.sgetn(frame.name, 16); frame.trans = Vector(frame.trans.y, frame.trans.z, frame.trans.x); frame.scale = Vector(frame.scale.y, frame.scale.z, frame.scale.x); //read frame verts... in.sgetn((char*)md2_verts, header.numVertices * 4); frame.vertices.resize(verts.size()); for (int j = 0; j < verts.size(); ++j) { Vertex& v = frame.vertices[j]; const VertInfo& i = verts[j]; const MD2Vertex& m = md2_verts[i.index]; v.x = m.y; v.y = m.z; v.z = m.x; v.u = i.u; v.v = i.v; v.n = m.n; } } delete md2_verts; //create initial mesh mesh = gx_graphics->createMesh(verts.size(), triangles.size(), 0); mesh->lock(); for (k = 0; k < triangles.size(); ++k) { const Triangle& t = triangles[k]; mesh->setTriangle(k, t.v[0], t.v[2], t.v[1]); } mesh->unlock(); //calculate bounding box. for (k = 0; k < header.numFrames; ++k) { const Frame& frame = frames[k]; const Vector& scale = frame.scale; const Vector& trans = frame.trans; for (int n = 0; n < frame.vertices.size(); ++n) { const Vertex& v = frame.vertices[n]; box.update(Vector(v.x, v.y, v.z) * scale + trans); } } if (!normals) { normals = (Vector*)md2norms; for (int k = 0; k < sizeof(md2norms) / 12; ++k) { normals[k] = Vector(normals[k].y, normals[k].z, normals[k].x); } } #pragma pack(push, 1) struct Header { int magic; int version; int skinWidth; int skinHeight; int frameSize; int numSkins; int numVertices; int numTexCoords; int numTriangles; int numGlCommands; int numFrames; int offsetSkins; int offsetTexCoords; int offsetTriangles; int offsetFrames; int offsetGlCommands; int offsetEnd; }; struct MD2Vertex { unsigned char x, y, z, n; }; struct Vertex { unsigned char x, y, z, u, v, n; }; struct Triangle { unsigned short v[3]; //index into vertices }; struct Frame { Vector scale; Vector trans; char name[16]; vector vertices; }; struct TexCoord { short s, t; }; struct VertInfo { unsigned short index; unsigned char u, v; VertInfo(unsigned short i, char u, char v) : index(i), u(u), v(v) {} bool operator<(const VertInfo& t) const { if (index < t.index) return true; if (t.index < index) return false; if (u < t.u) return true; if (t.u < u) return false; return v < t.v; } }; #pragma pack(pop) int ref_cnt; Header header; vector frames; gxMesh* mesh; Box box; Rep(const string& file); ~Rep(); void render(MD2Model * model, float render_t, int render_a, int render_b); }; MD2Model::Rep::Rep(const string& file) : ref_cnt(1), mesh(0) { filebuf in; if (!in.open(file.c_str(), ios_base::in | ios_base::binary)) { return; } if (in.sgetn((char*)&header, sizeof(header)) != sizeof(header)) { return; } if (header.magic != '2PDI' || header.version != 8) { return; } int k; //read tex coords in.pubseekpos(header.offsetTexCoords); TexCoord* coords = d_new TexCoord[header.numTexCoords]; in.sgetn((char*)coords, header.numTexCoords * sizeof(TexCoord)); vector verts; map info_map; //build triangles vector triangles; in.pubseekpos(header.offsetTriangles); triangles.resize(header.numTriangles); for (k = 0; k < triangles.size(); ++k) { unsigned short v[3], t[3]; in.sgetn((char*)v, 6); in.sgetn((char*)t, 6); for (int j = 0; j < 3; ++j) { unsigned char tu = coords[t[j]].s * 256.0f / header.skinWidth; unsigned char tv = coords[t[j]].t * 256.0f / header.skinHeight; VertInfo i(v[j], tu, tv); map::iterator it = info_map.find(i); if (it == info_map.end()) { info_map[i] = triangles[k].v[j] = verts.size(); verts.push_back(i); } else { triangles[k].v[j] = it->second; } } } delete coords; //load frames string tt = "MD2 Frames:" + itoa(header.numFrames); gx_runtime->debugLog(tt.c_str()); in.pubseekpos(header.offsetFrames); frames.resize(header.numFrames); MD2Vertex* md2_verts = d_new MD2Vertex[header.numVertices]; for (k = 0; k < frames.size(); ++k) { Frame& frame = frames[k]; //read frame header; in.sgetn((char*)&frame.scale, 12); in.sgetn((char*)&frame.trans, 12); in.sgetn(frame.name, 16); frame.trans = Vector(frame.trans.y, frame.trans.z, frame.trans.x); frame.scale = Vector(frame.scale.y, frame.scale.z, frame.scale.x); //read frame verts... in.sgetn((char*)md2_verts, header.numVertices * 4); frame.vertices.resize(verts.size()); for (int j = 0; j < verts.size(); ++j) { Vertex& v = frame.vertices[j]; const VertInfo& i = verts[j]; const MD2Vertex& m = md2_verts[i.index]; v.x = m.y; v.y = m.z; v.z = m.x; v.u = i.u; v.v = i.v; v.n = m.n; } } delete md2_verts; //create initial mesh mesh = gx_graphics->createMesh(verts.size(), triangles.size(), 0); mesh->lock(); for (k = 0; k < triangles.size(); ++k) { const Triangle& t = triangles[k]; mesh->setTriangle(k, t.v[0], t.v[2], t.v[1]); } mesh->unlock(); //calculate bounding box. for (k = 0; k < header.numFrames; ++k) { const Frame& frame = frames[k]; const Vector& scale = frame.scale; const Vector& trans = frame.trans; for (int n = 0; n < frame.vertices.size(); ++n) { const Vertex& v = frame.vertices[n]; box.update(Vector(v.x, v.y, v.z) * scale + trans); } } if (!normals) { normals = (Vector*)md2norms; for (int k = 0; k < sizeof(md2norms) / 12; ++k) { normals[k] = Vector(normals[k].y, normals[k].z, normals[k].x); } } } MD2Model::Rep::~Rep() { if (mesh) gx_graphics->freeMesh(mesh); } void MD2Model::Rep::render(MD2Model* model, float render_t, int render_a, int render_b) { const Frame& frame_a = frames[render_a]; const Vector& scale_a = frame_a.scale; const Vector& trans_a = frame_a.trans; const Frame& frame_b = frames[render_b]; const Vector& scale_b = frame_b.scale; const Vector& trans_b = frame_b.trans; mesh->lock(); int k; for (k = 0; k < frame_a.vertices.size(); ++k) { const Vertex& v_a = frame_a.vertices[k]; const Vector& n_a = normals[v_a.n]; Vector t_a(v_a.x * scale_a.x + trans_a.x, v_a.y * scale_a.y + trans_a.y, v_a.z * scale_a.z + trans_a.z); const Vertex& v_b = frame_b.vertices[k]; const Vector& n_b = normals[v_b.n]; Vector t_b(v_b.x * scale_b.x + trans_b.x, v_b.y * scale_b.y + trans_b.y, v_b.z * scale_b.z + trans_b.z); Vector t = (t_b - t_a) * render_t + t_a; Vector n = (n_b - n_a) * render_t + n_a; float tex_coords[] = {v_a.u / 256.0f, v_a.v / 256.0f, 1, 0, 0, 1}; mesh->setVertex(k, &t.x, &n.x, white, tex_coords); } mesh->unlock(); model->enqueue(mesh, 0, frame_a.vertices.size(), 0, header.numTriangles); } MD2Model::MD2Model(const string& f) : rep(d_new Rep(f)), anim_mode(0), anim_time(0), render_a(0), render_b(0), render_t(0) {} MD2Model::MD2Model(const MD2Model& t) : Model(t), rep(t.rep), anim_mode(0), anim_time(0), render_a(0), render_b(0), render_t(0) { ++rep->ref_cnt; } MD2Model::~MD2Model() { if (!--rep->ref_cnt) delete rep; } void MD2Model::startMD2Anim(int first, int last, int mode, float speed) { if (!speed && !mode) { anim_mode = 0; return; } if (first < 0) first = 0; else if (first >= rep->header.numFrames) first = rep->header.numFrames - 1; if (last < 0) last = 0; else if (last >= rep->header.numFrames) last = rep->header.numFrames - 1; if (first == last) { anim_mode = 0; render_a = render_b = first; render_t = 0; return; } if (last < first) std::swap(first, last); anim_first = first; anim_last = last; anim_len = last - first; anim_speed = speed; anim_time = speed > 0 ? first : last; anim_mode = mode; } void MD2Model::animate(float e) { Model::animate(e); if (!anim_mode) return; anim_time = anim_time + anim_speed * e; if (anim_time < anim_first) { switch (anim_mode) { case ANIM_MODE_LOOP: anim_time += anim_len; break; case ANIM_MODE_PINGPONG: anim_time = anim_first + (anim_first - anim_time); anim_speed = -anim_speed; break; default: anim_time = anim_first; anim_mode = 0; break; } } else if (anim_time >= anim_last) { switch (anim_mode) { case ANIM_MODE_LOOP: anim_time -= anim_len; break; case ANIM_MODE_PINGPONG: anim_time = anim_last - (anim_time - anim_last); anim_speed = -anim_speed; break; default: anim_time = anim_last; anim_mode = 0; break; } } render_a = floor(anim_time); render_b = render_a + 1; if (anim_mode == ANIM_MODE_LOOP && render_b == anim_last) render_b = anim_first; render_t = anim_time - render_a; } void MD2Model::render(const RenderContext& rc) { static Frustum f; new (&f) Frustum(rc.getWorldFrustum(), -getRenderTform()); if (!f.cull(rep->box)) return; rep->render(this, render_t, render_a, render_b); } int MD2Model::getMD2AnimLength() const { return rep->frames.size(); } bool MD2Model::getValid() const { return rep->mesh != 0; }