/* 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) if(!(expression)) { globalErrorStream() << "md5 parse failed: " #expression "\n"; return false; } else 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); }