// Copyright (C) 2014-2015 ChaosForge Ltd // http://chaosforge.org/ // // This file is part of Nova libraries. // For conditions of distribution and use, see copying.txt file in root folder. #include "nv/formats/nmd_loader.hh" #include "nv/io/std_stream.hh" #include "nv/stl/string.hh" #include "nv/interface/data_channel_access.hh" using namespace nv; bool nv::nmd_loader::load( stream& source ) { // TODO: proper error handling reset(); nmd_header root_header; source.read( &root_header, sizeof( root_header ), 1 ); skip_attributes( source, root_header.attributes ); for ( uint32 i = 0; i < root_header.elements; ++i ) { nmd_element_header element_header; source.read( &element_header, sizeof( element_header ), 1 ); skip_attributes( source, element_header.attributes ); switch ( element_header.type ) { case nmd_type::MESH : load_mesh( source, element_header ); break; case nmd_type::ANIMATION : load_animation( source, element_header ); break; case nmd_type::STRINGS : load_strings( source ); break; default: NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" ); break; } } return true; } bool nv::nmd_loader::load_mesh( stream& source, const nmd_element_header& e ) { data_channel_set* mesh = data_channel_set_creator::create_set( e.children ); load_channel_set( source, mesh, e ); // m_mesh_names.push_back( e.name ); m_meshes.push_back( mesh ); return true; } data_channel_set* nv::nmd_loader::release_mesh_data( size_t index ) { data_channel_set* result = m_meshes[ index ]; m_meshes[ index ] = nullptr; return result; } mesh_data_pack* nv::nmd_loader::release_mesh_data_pack() { uint32 size = m_meshes.size(); data_channel_set* meshes = data_channel_set_creator::create_set_array( size, 0 ); for ( uint32 i = 0; i < size; ++i ) { meshes[i] = move( *m_meshes[i] ); delete m_meshes[i]; } m_meshes.clear(); return new mesh_data_pack( size, meshes, release_mesh_nodes_data() ); } void nv::nmd_loader::reset() { for ( auto mesh : m_meshes ) if ( mesh ) delete mesh; if ( m_node_data ) delete m_node_data; m_meshes.clear(); m_node_data = nullptr; } void nv::nmd_loader::skip_attributes( stream& source, uint32 count ) { if ( count == 0 ) return; source.seek( count * sizeof( nmd_attribute ), origin::CUR ); } nv::nmd_loader::~nmd_loader() { reset(); } bool nv::nmd_loader::load_strings( stream& source ) { if ( !m_strings ) return true; // TODO: load strings optionally string_table* strings = new string_table( source ); m_strings->insert( strings ); delete strings; return true; } bool nv::nmd_loader::load_animation( stream& source, const nmd_element_header& e ) { NV_ASSERT( m_node_data == nullptr, "MULTIPLE NODE ENTRIES!" ); nmd_animation_header animation_header; source.read( &animation_header, sizeof( animation_header ), 1 ); m_node_data = new mesh_nodes_data( e.name, animation_header.frame_rate, animation_header.duration, animation_header.flat ); for ( uint32 i = 0; i < e.children; ++i ) { nmd_element_header element_header; source.read( &element_header, sizeof( element_header ), 1 ); skip_attributes( source, element_header.attributes ); NV_ASSERT( element_header.type == nmd_type::NODE, "NODE expected!" ); data_channel_set* set = data_channel_set_creator::create_set( element_header.children ); load_channel_set( source, set, element_header ); m_node_data->push_back( set ); } return true; } bool nv::nmd_loader::load_channel( stream& source, data_channel_set* channel_set ) { data_channel_set_creator kaccess( channel_set ); nmd_channel_header cheader; source.read( &cheader, sizeof( cheader ), 1 ); raw_data_channel_access channel( kaccess.add_channel( cheader.format, cheader.count ) ); source.read( channel.raw_data(), channel.element_size(), channel.size() ); return true; } bool nv::nmd_loader::load_channel_set( stream& source, data_channel_set* channel_set, const nmd_element_header& e ) { data_channel_set_creator kaccess( channel_set ); for ( uint32 c = 0; c < e.children; ++c ) { load_channel( source, channel_set ); } data_channel_set_creator access( channel_set ); access.set_name( e.name ); access.set_parent_id( e.parent_id ); access.set_transform( e.transform ); return true; } mesh_nodes_data* nv::nmd_loader::release_mesh_nodes_data( size_t ) { if ( m_node_data ) { mesh_nodes_data* result = m_node_data; m_node_data = nullptr; return result; } return nullptr; } // ---------------------------------------------------------------- // nmd format dump // HACK : TEMPORARY - will go to it's own file, probably nmd_io void nv::nmd_dump_header( stream& stream_out, uint32 elements, uint64 name ) { nmd_header header; header.id = four_cc<'n', 'm', 'f', '1'>::value; header.elements = elements; // +1 string array header.name = name; header.version = 1; header.attributes = 0; stream_out.write( &header, sizeof( header ), 1 ); } void nv::nmd_dump_element( stream& stream_out, const data_channel_set& data, nmd_type type ) { uint32 size = 0; for ( auto& chan : data ) { size += sizeof( nmd_channel_header ); size += chan.raw_size(); } nmd_element_header eheader; eheader.type = type; eheader.children = static_cast( data.size() ); eheader.size = size; eheader.name = data.get_name(); eheader.transform = data.get_transform(); eheader.parent_id = data.get_parent_id(); eheader.attributes = 0; stream_out.write( &eheader, sizeof( eheader ), 1 ); for ( auto& channel : data ) { nmd_channel_header cheader; cheader.format = channel.descriptor(); cheader.count = channel.size(); stream_out.write( &cheader, sizeof( cheader ), 1 ); stream_out.write( channel.raw_data(), channel.element_size(), channel.size() ); } } void nv::nmd_dump_nodes( stream& stream_out, const mesh_nodes_data& nodes ) { uint32 total = sizeof( nmd_animation_header ); for ( auto node : nodes ) { total += sizeof( nmd_element_header ); for ( uint32 c = 0; c < node->size(); ++c ) { total += sizeof( nmd_channel_header ); total += node->get_channel( c )->raw_size(); } } nmd_element_header header; header.type = nmd_type::ANIMATION; header.children = static_cast( nodes.get_count() ); header.size = total; header.name = nodes.get_name(); header.transform = mat4(); header.parent_id = -1; header.attributes = 0; stream_out.write( &header, sizeof( header ), 1 ); nmd_animation_header aheader; aheader.frame_rate = nodes.get_frame_rate(); aheader.duration = nodes.get_duration(); aheader.flat = nodes.is_flat(); stream_out.write( &aheader, sizeof( aheader ), 1 ); for ( auto node : nodes ) { nmd_dump_element( stream_out, *node, nv::nmd_type::NODE ); } } void nv::nmd_dump_strings( stream& stream_out, const string_table& strings ) { nmd_element_header sheader; sheader.type = nv::nmd_type::STRINGS; sheader.children = 0; sheader.size = strings.dump_size(); sheader.name = 0; sheader.parent_id = -1; sheader.attributes = 0; stream_out.write( &sheader, sizeof( sheader ), 1 ); strings.dump( stream_out ); } void nv::nmd_dump( stream& stream_out, const mesh_data_pack* model, const string_table* strings, uint64 name ) { uint32 elements = ( strings ? 1 : 0 ) // +1 string array + model->get_count() // meshes + ( model->get_node_count() > 0 ? 1 : 0 ); // nodes nmd_dump_header( stream_out, elements, name ); for ( uint32 i = 0; i < model->get_count(); ++i ) { nmd_dump_element( stream_out, *model->get_mesh( i ), nv::nmd_type::MESH ); } if ( model->get_node_count() > 0 ) { nmd_dump_nodes( stream_out, *model->get_nodes() ); } if ( strings ) { nmd_dump_strings( stream_out, *strings ); } }