// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz // http://chaosforge.org/ // // This file is part of NV Libraries. // For conditions of distribution and use, see copyright notice in nv.hh #include "nv/formats/md2_loader.hh" #include #include "nv/logging.hh" #include using namespace nv; // based on http://tfc.duke.free.fr/coding/md2-specs-en.html // assuming low-endian #define MD2_MAX_FRAMES 512 #define MD2_MAX_SKINS 32 #define MD2_MAX_VERTS 2048 #define MD2_MAX_TRIANGLES 4096 #define MD2_MAX_TEXCOORDS 2048 #define MD2_NORMAL_COUNT 162 typedef float md2_vec3_t[3]; static const md2_vec3_t md2_normal_table[MD2_NORMAL_COUNT] = { #include "src/formats/md2_normals.inc" }; struct md2_header_t { char magic[4]; int version; int skinwidth; int skinheight; int framesize; int num_skins; int num_vertices; int num_st; int num_tris; int num_glcmds; int num_frames; int offset_skins; int offset_st; int offset_tris; int offset_frames; int offset_glcmds; int offset_end; }; struct md2_skin_t { char name[64]; }; struct md2_texcoord_t { short s; short t; }; struct md2_triangle_t { unsigned short vertex[3]; unsigned short st[3]; }; struct md2_vertex_t { uint8 v[3]; uint8 n; }; struct md2_frame_t { md2_vec3_t scale; md2_vec3_t translate; char name[16]; md2_vertex_t *vertices; }; struct md2_glcmd_t { float s; float t; int index; }; struct md2_t { md2_header_t header; md2_skin_t* skins; md2_texcoord_t* texcoords; md2_triangle_t* triangles; md2_frame_t* frames; int* glcmds; }; static bool check_md2_magic( char* magic ) { return magic[0] == 'I' && magic[1] == 'D' && magic[2] == 'P' && magic[3] == '2'; } static void free_md2_frame( md2_frame_t* frame ) { delete[] frame->vertices; } static void free_md2( md2_t* md2 ) { delete[] md2->skins; delete[] md2->texcoords; delete[] md2->triangles; delete[] md2->glcmds; for ( int i = 0; i < md2->header.num_frames; ++i ) { free_md2_frame( &(md2->frames[i]) ); } delete[] md2->frames; } static bool read_md2_frame( md2_frame_t* frame, unsigned vcount, nv::stream& source ) { frame->vertices = new md2_vertex_t[ vcount ]; source.read( frame->scale, sizeof(md2_vec3_t), 1 ); source.read( frame->translate, sizeof(md2_vec3_t), 1 ); source.read( frame->name, sizeof(char), 16 ); source.read( frame->vertices, sizeof(md2_vertex_t), vcount ); return true; } static bool read_md2( md2_t* md2, nv::stream& source ) { md2->frames = nullptr; md2->skins = nullptr; md2->texcoords = nullptr; md2->triangles = nullptr; md2->glcmds = nullptr; source.read( &(md2->header), sizeof(md2_header_t), 1 ); if ( !check_md2_magic( md2->header.magic ) || md2->header.num_skins > MD2_MAX_SKINS || md2->header.num_vertices > MD2_MAX_VERTS || md2->header.num_st > MD2_MAX_TEXCOORDS || md2->header.num_tris > MD2_MAX_TRIANGLES || md2->header.num_frames > MD2_MAX_FRAMES ) { return false; } NV_LOG( LOG_INFO, "num_skins = " << md2->header.num_skins ); NV_LOG( LOG_INFO, "num_vertices = " << md2->header.num_vertices ); NV_LOG( LOG_INFO, "num_st = " << md2->header.num_st ); NV_LOG( LOG_INFO, "num_tris = " << md2->header.num_tris ); NV_LOG( LOG_INFO, "num_frames = " << md2->header.num_frames ); md2->skins = new md2_skin_t [ md2->header.num_skins ]; md2->texcoords = new md2_texcoord_t[ md2->header.num_st ]; md2->triangles = new md2_triangle_t[ md2->header.num_tris ]; md2->glcmds = new int [ md2->header.num_glcmds ]; source.seek( md2->header.offset_skins, origin::SET ); source.read( md2->skins, sizeof(md2_skin_t), static_cast( md2->header.num_skins ) ); source.seek( md2->header.offset_st, origin::SET ); source.read( md2->texcoords, sizeof(md2_texcoord_t), static_cast( md2->header.num_st ) ); source.seek( md2->header.offset_tris, origin::SET ); source.read( md2->triangles, sizeof(md2_triangle_t), static_cast( md2->header.num_tris ) ); source.seek( md2->header.offset_glcmds, origin::SET); source.read( md2->glcmds, sizeof(int), static_cast( md2->header.num_glcmds ) ); md2->frames = new md2_frame_t [ md2->header.num_frames ]; source.seek( md2->header.offset_frames, origin::SET ); for ( int i = 0; i < md2->header.num_frames; ++i ) { if (!read_md2_frame( &(md2->frames[i]), static_cast( md2->header.num_vertices ), source ) ) return false; } return true; } static inline vec3 md2_vec3( const md2_vec3_t& v ) { // return vec3( v[0], v[1], v[2] ); return vec3( v[0], v[2], v[1] ); } static inline vec3 md2_normal( uint8 normal ) { return md2_vec3( md2_normal_table[normal] ); } static vec3 s_md2_normal_cache[MD2_NORMAL_COUNT]; static bool s_md2_normal_ready = false; md2_loader::md2_loader() : m_md2( nullptr ), m_size( 0 ) { if ( !s_md2_normal_ready ) { for ( int i = 0; i < MD2_NORMAL_COUNT; ++i ) { s_md2_normal_cache[i].x = md2_normal_table[i][0]; // s_md2_normal_cache[i].y = md2_normal_table[i][1]; // s_md2_normal_cache[i].z = md2_normal_table[i][2]; s_md2_normal_cache[i].y = md2_normal_table[i][2]; s_md2_normal_cache[i].z = md2_normal_table[i][1]; } } } md2_loader::~md2_loader() { if (m_md2 != nullptr) { free_md2( (md2_t*)(m_md2) ); delete (md2_t*)m_md2; } } bool md2_loader::load( stream& source ) { m_size = 0; m_md2 = (void*)(new md2_t); if ( !read_md2( (md2_t*)m_md2, source ) ) { return false; } reindex(); m_size = m_new_indexes.size(); return true; } mesh* nv::md2_loader::release_mesh() { return get_frame( 0 ); } mesh* nv::md2_loader::get_frame( sint32 frame ) { md2_t* md2 = (md2_t*)m_md2; if ( md2 == nullptr || frame >= md2->header.num_frames ) return nullptr; mesh* m = new mesh(); vertex_attribute< vec3 >* position = m->add_attribute("nv_position"); vertex_attribute< vec3 >* normal = m->add_attribute("nv_normal"); vertex_attribute< vec2 >* texcoord = m->add_attribute("nv_texcoord"); vertex_attribute< uint16 >* indices = m->add_indices(); load_positions( position->get(), frame ); load_normals( normal->get(), frame ); load_texcoords( texcoord->get() ); load_indicies( indices->get() ); m_size = indices->get().size(); return m; } size_t md2_loader::get_max_frames() const { return static_cast< size_t >( ((md2_t*)m_md2)->header.num_frames ); } void md2_loader::load_positions( std::vector& p, sint32 frame /*=-1*/ ) { md2_t* md2 = (md2_t*)m_md2; size_t num_frames = static_cast< size_t >( md2->header.num_frames ); size_t num_verts = m_new_vindexes.size(); p.clear(); size_t current_frame = ( frame == -1 ? 0 : static_cast< size_t >( frame ) ); size_t frame_count = ( frame == -1 ? num_frames : 1 ); p.reserve( num_verts * frame_count ); while ( frame_count > 0 ) { const md2_frame_t& cframe = md2->frames[current_frame]; NV_LOG( LOG_INFO, "FrameID = " << cframe.name ); vec3 scale = md2_vec3( cframe.scale ); vec3 translate = md2_vec3( cframe.translate ); for (size_t i = 0; i < num_verts; ++i ) { const md2_vertex_t& v = cframe.vertices[ m_new_vindexes[ i ] ]; p.push_back( vec3( v.v[0], v.v[2], v.v[1] ) * scale + translate ); } ++current_frame; --frame_count; } } void md2_loader::load_normals( std::vector& n, sint32 frame /*=-1*/ ) { md2_t* md2 = (md2_t*)m_md2; size_t num_frames = static_cast< size_t >( md2->header.num_frames ); size_t num_verts = m_new_vindexes.size(); n.clear(); size_t current_frame = ( frame == -1 ? 0 : static_cast< size_t>( frame ) ); size_t frame_count = ( frame == -1 ? num_frames : 1 ); n.reserve( num_verts * frame_count ); while ( frame_count > 0 ) { const md2_frame_t& cframe = md2->frames[current_frame]; for (size_t i = 0; i < num_verts; ++i ) { const md2_vertex_t& v = cframe.vertices[ m_new_vindexes[ i ] ]; n.push_back( s_md2_normal_cache[ v.n ] ); } ++current_frame; --frame_count; } } void md2_loader::load_texcoords( std::vector& t ) { md2_t* md2 = (md2_t*)m_md2; size_t num_verts = m_new_vindexes.size(); t.clear(); t.reserve( num_verts ); vec2 scale( 1.0f / (float) md2->header.skinwidth, 1.0f / (float) md2->header.skinheight ); for (size_t i = 0; i < num_verts; ++i ) { const md2_texcoord_t& st = md2->texcoords[ m_new_tindexes[ i ] ]; t.push_back( scale * vec2( st.s, st.t ) ); } } void md2_loader::load_indicies( std::vector& idx ) { idx.assign( m_new_indexes.begin(), m_new_indexes.end() ); } void nv::md2_loader::reindex() { md2_t* md2 = (md2_t*)m_md2; uint32 num_indexes = static_cast< uint32 >( md2->header.num_tris * 3 ); uint32 stats_reuse = 0; uint32 stats_collision = 0; std::vector< sint32 > index_translation( md2->header.num_vertices, -1 ); m_new_indexes.clear(); m_new_indexes.reserve( num_indexes ); m_new_vindexes.reserve( num_indexes ); m_new_tindexes.reserve( num_indexes ); uint16 index_count = 0; for ( int i = 0; i < md2->header.num_tris; ++i ) { const md2_triangle_t& t = md2->triangles[i]; for ( int j = 0; j < 3; ++j ) { uint16 index = t.vertex[j]; uint16 tindex = t.st[j]; if ( index_translation[ index ] != -1 ) { uint16 prev = static_cast< uint16 >( index_translation[ index ] ); if ( m_new_tindexes[ prev ] == tindex ) { m_new_indexes.push_back( prev ); stats_reuse++; continue; } } m_new_vindexes.push_back( index ); m_new_tindexes.push_back( tindex ); m_new_indexes.push_back( index_count ); if ( index_translation[ index ] == -1 ) { index_translation[ index ] = index_count; } else { stats_collision++; } index_count++; } } NV_LOG( LOG_INFO, "New vertex count = " << m_new_vindexes.size() ); NV_LOG( LOG_INFO, "Collisions = " << stats_collision ); NV_LOG( LOG_INFO, "Reuse count = " << stats_reuse ); }