/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "md5.h" #include "iscriplib.h" #include "imodel.h" #include "archivelib.h" #include "stringio.h" #include "model.h" #define MD5_RETURN_FALSE_IF_FAIL(expression) do { if (!(expression)) { globalErrorStream() << "md5 parse failed: " #expression "\n"; return false; } } while (0) bool MD5_parseToken(Tokeniser &tokeniser, const char *string) { const char *token = tokeniser.getToken(); MD5_RETURN_FALSE_IF_FAIL(token != 0); return string_equal(token, string); } bool MD5_parseFloat(Tokeniser &tokeniser, float &f) { const char *token = tokeniser.getToken(); MD5_RETURN_FALSE_IF_FAIL(token != 0); return string_parse_float(token, f); } bool MD5_parseString(Tokeniser &tokeniser, const char *&s) { const char *token = tokeniser.getToken(); MD5_RETURN_FALSE_IF_FAIL(token != 0); s = token; return true; } bool MD5_parseInteger(Tokeniser &tokeniser, int &i) { const char *token = tokeniser.getToken(); MD5_RETURN_FALSE_IF_FAIL(token != 0); return string_parse_int(token, i); } bool MD5_parseSize(Tokeniser &tokeniser, std::size_t &i) { const char *token = tokeniser.getToken(); MD5_RETURN_FALSE_IF_FAIL(token != 0); return string_parse_size(token, i); } bool MD5_parseVector3(Tokeniser &tokeniser, Vector3 &v) { MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "(")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseFloat(tokeniser, v.x())); MD5_RETURN_FALSE_IF_FAIL(MD5_parseFloat(tokeniser, v.y())); MD5_RETURN_FALSE_IF_FAIL(MD5_parseFloat(tokeniser, v.z())); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, ")")); return true; } template inline Element float_squared(const Element &f) { return f * f; } class MD5Joint { public: int parent; Vector3 position; Vector4 rotation; }; typedef Array MD5Joints; class MD5Vert { public: std::size_t index; float u; float v; std::size_t weight_index; std::size_t weight_count; }; typedef Array MD5Verts; class MD5Tri { public: std::size_t index; std::size_t a; std::size_t b; std::size_t c; }; typedef Array MD5Tris; class MD5Weight { public: std::size_t index; std::size_t joint; float t; Vector3 v; }; typedef Array MD5Weights; typedef float MD5Component; typedef Array MD5Components; class MD5Frame { public: MD5Components m_components; }; typedef Array MD5Weights; bool MD5_parseVersion(Tokeniser &tokeniser) { { const char *versionKey = tokeniser.getToken(); if (versionKey == 0 || !string_equal(versionKey, "MD5Version")) { globalErrorStream() << "not a valid md5 file\n"; return false; } } { const char *versionValue = tokeniser.getToken(); if (versionValue == 0 || !string_equal(versionValue, "10")) { globalErrorStream() << "only md5 version 10 supported\n"; return false; } } return true; } bool MD5Anim_parse(Tokeniser &tokeniser) { MD5_RETURN_FALSE_IF_FAIL(MD5_parseVersion(tokeniser)); tokeniser.nextLine(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "commandline")); const char *commandline; MD5_RETURN_FALSE_IF_FAIL(MD5_parseString(tokeniser, commandline)); tokeniser.nextLine(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numFrames")); std::size_t numFrames; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numFrames)); tokeniser.nextLine(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numJoints")); std::size_t numJoints; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numJoints)); tokeniser.nextLine(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "frameRate")); std::size_t frameRate; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, frameRate)); tokeniser.nextLine(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numAnimatedComponents")); std::size_t numAnimatedComponents; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numAnimatedComponents)); tokeniser.nextLine(); // parse heirarchy MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "hierarchy")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "{")); tokeniser.nextLine(); for (std::size_t i = 0; i < numJoints; ++i) { const char *name; MD5_RETURN_FALSE_IF_FAIL(MD5_parseString(tokeniser, name)); int parent; MD5_RETURN_FALSE_IF_FAIL(MD5_parseInteger(tokeniser, parent)); std::size_t flags; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, flags)); std::size_t index; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, index)); tokeniser.nextLine(); } MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "}")); tokeniser.nextLine(); // parse bounds MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "bounds")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "{")); tokeniser.nextLine(); for (std::size_t i = 0; i < numFrames; ++i) { Vector3 mins; MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, mins)); Vector3 maxs; MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, maxs)); tokeniser.nextLine(); } MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "}")); tokeniser.nextLine(); // parse baseframe MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "baseframe")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "{")); tokeniser.nextLine(); for (std::size_t i = 0; i < numJoints; ++i) { Vector3 position; MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, position)); Vector3 rotation; MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, rotation)); tokeniser.nextLine(); } MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "}")); tokeniser.nextLine(); // parse frames for (std::size_t i = 0; i < numFrames; ++i) { MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "frame")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "{")); tokeniser.nextLine(); for (std::size_t i = 0; i < numAnimatedComponents; ++i) { float component; MD5_RETURN_FALSE_IF_FAIL(MD5_parseFloat(tokeniser, component)); tokeniser.nextLine(); } MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "}")); tokeniser.nextLine(); } return true; } bool MD5Model_parse(Model &model, Tokeniser &tokeniser) { MD5_RETURN_FALSE_IF_FAIL(MD5_parseVersion(tokeniser)); tokeniser.nextLine(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "commandline")); const char *commandline; MD5_RETURN_FALSE_IF_FAIL(MD5_parseString(tokeniser, commandline)); tokeniser.nextLine(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numJoints")); std::size_t numJoints; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numJoints)); tokeniser.nextLine(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numMeshes")); std::size_t numMeshes; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numMeshes)); tokeniser.nextLine(); MD5Joints joints(numJoints); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "joints")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "{")); tokeniser.nextLine(); for (MD5Joints::iterator i = joints.begin(); i != joints.end(); ++i) { const char *jointName; MD5_RETURN_FALSE_IF_FAIL(MD5_parseString(tokeniser, jointName)); MD5_RETURN_FALSE_IF_FAIL(MD5_parseInteger(tokeniser, (*i).parent)); MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, (*i).position)); MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, vector4_to_vector3((*i).rotation))); (*i).rotation.w() = -static_cast( sqrt(1.0f - (float_squared((*i).rotation.x()) + float_squared((*i).rotation.y()) + float_squared((*i).rotation.z())))); tokeniser.nextLine(); } MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "}")); tokeniser.nextLine(); for (std::size_t i = 0; i < numMeshes; ++i) { Surface &surface = model.newSurface(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "mesh")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "{")); tokeniser.nextLine(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "shader")); const char *shader; MD5_RETURN_FALSE_IF_FAIL(MD5_parseString(tokeniser, shader)); surface.setShader(shader); tokeniser.nextLine(); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numverts")); std::size_t numVerts; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numVerts)); tokeniser.nextLine(); MD5Verts verts(numVerts); for (MD5Verts::iterator j = verts.begin(); j != verts.end(); ++j) { MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "vert")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, (*j).index)); MD5_RETURN_FALSE_IF_FAIL((*j).index == std::size_t(j - verts.begin())); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "(")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseFloat(tokeniser, (*j).u)); MD5_RETURN_FALSE_IF_FAIL(MD5_parseFloat(tokeniser, (*j).v)); MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, ")")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, (*j).weight_index)); MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, (*j).weight_count)); tokeniser.nextLine(); } MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numtris")); std::size_t numTris; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numTris)); tokeniser.nextLine(); MD5Tris tris(numTris); for (MD5Tris::iterator j = tris.begin(); j != tris.end(); ++j) { MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "tri")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, (*j).index)); MD5_RETURN_FALSE_IF_FAIL((*j).index == std::size_t(j - tris.begin())); MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, (*j).a)); MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, (*j).b)); MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, (*j).c)); tokeniser.nextLine(); } MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "numweights")); std::size_t numWeights; MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, numWeights)); tokeniser.nextLine(); MD5Weights weights(numWeights); for (MD5Weights::iterator j = weights.begin(); j != weights.end(); ++j) { MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "weight")); MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, (*j).index)); MD5_RETURN_FALSE_IF_FAIL((*j).index == std::size_t(j - weights.begin())); MD5_RETURN_FALSE_IF_FAIL(MD5_parseSize(tokeniser, (*j).joint)); MD5_RETURN_FALSE_IF_FAIL(MD5_parseFloat(tokeniser, (*j).t)); MD5_RETURN_FALSE_IF_FAIL(MD5_parseVector3(tokeniser, (*j).v)); tokeniser.nextLine(); } MD5_RETURN_FALSE_IF_FAIL(MD5_parseToken(tokeniser, "}")); tokeniser.nextLine(); for (MD5Verts::iterator j = verts.begin(); j != verts.end(); ++j) { MD5Vert &vert = (*j); Vector3 skinned(0, 0, 0); for (std::size_t k = 0; k != vert.weight_count; ++k) { MD5Weight &weight = weights[vert.weight_index + k]; MD5Joint &joint = joints[weight.joint]; skinned += (quaternion_transformed_point(joint.rotation, weight.v) + joint.position) * weight.t; } surface.vertices().push_back( ArbitraryMeshVertex(vertex3f_for_vector3(skinned), Normal3f(0, 0, 0), TexCoord2f(vert.u, vert.v))); } for (MD5Tris::iterator j = tris.begin(); j != tris.end(); ++j) { MD5Tri &tri = (*j); surface.indices().insert(RenderIndex(tri.a)); surface.indices().insert(RenderIndex(tri.b)); surface.indices().insert(RenderIndex(tri.c)); } for (Surface::indices_t::iterator j = surface.indices().begin(); j != surface.indices().end(); j += 3) { ArbitraryMeshVertex &a = surface.vertices()[*(j + 0)]; ArbitraryMeshVertex &b = surface.vertices()[*(j + 1)]; ArbitraryMeshVertex &c = surface.vertices()[*(j + 2)]; Vector3 weightedNormal( vector3_cross( reinterpret_cast( c.vertex ) - reinterpret_cast( a.vertex ), reinterpret_cast( b.vertex ) - reinterpret_cast( a.vertex ) ) ); reinterpret_cast( a.normal ) += weightedNormal; reinterpret_cast( b.normal ) += weightedNormal; reinterpret_cast( c.normal ) += weightedNormal; } for (Surface::vertices_t::iterator j = surface.vertices().begin(); j != surface.vertices().end(); ++j) { vector3_normalise(reinterpret_cast((*j).normal )); } surface.updateAABB(); } model.updateAABB(); return true; } void MD5Model_construct(Model &model, TextInputStream &inputStream) { Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser(inputStream); MD5Model_parse(model, tokeniser); tokeniser.release(); } scene::Node &MD5Model_new(TextInputStream &inputStream) { ModelNode *modelNode = new ModelNode(); MD5Model_construct(modelNode->model(), inputStream); return modelNode->node(); } scene::Node &loadMD5Model(ArchiveFile &file) { BinaryToTextInputStream inputStream(file.getInputStream()); return MD5Model_new(inputStream); }