// Copyright (C) 2014 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/nmd_loader.hh" #include "nv/io/std_stream.hh" #include "nv/string.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 ); for ( uint32 i = 0; i < root_header.elements; ++i ) { nmd_element_header element_header; source.read( &element_header, sizeof( element_header ), 1 ); 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::STRING_TABLE : 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 ) { mesh_data* mesh = new mesh_data(); for ( uint32 s = 0; s < e.children; ++s ) { nmd_element_header element_header; source.read( &element_header, sizeof( element_header ), 1 ); NV_ASSERT( element_header.type == nmd_type::STREAM, "STREAM expected!" ); nmd_stream_header stream_header; source.read( &stream_header, sizeof( stream_header ), 1 ); mesh_raw_channel* channel = mesh_raw_channel::create( stream_header.format, stream_header.count ); source.read( channel->data, stream_header.format.size, stream_header.count ); mesh->add_channel( channel ); } m_mesh_names.push_back( e.name ); m_meshes.push_back( mesh ); return true; } mesh_data* nv::nmd_loader::release_mesh_data( size_t index ) { mesh_data* result = m_meshes[ index ]; if ( m_strings ) result->set_name( m_strings->get( m_mesh_names[ index ] ) ); m_meshes[ index ] = nullptr; return result; } mesh_data_pack* nv::nmd_loader::release_mesh_data_pack() { uint32 size = m_meshes.size(); mesh_data* meshes = new mesh_data[ size ]; for ( uint32 i = 0; i < size; ++i ) { m_meshes[i]->move_to( 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_strings ) delete m_strings; if ( m_node_data ) delete m_node_data; m_meshes.clear(); m_mesh_names.clear(); m_node_names.clear(); m_node_data = nullptr; m_node_array = nullptr; m_strings = nullptr; } nv::nmd_loader::~nmd_loader() { reset(); } bool nv::nmd_loader::load_strings( stream& source ) { NV_ASSERT( m_strings == nullptr, "MULTIPLE STRING ENTRIES!" ); m_strings = new string_table( &source ); 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_array = new mesh_node_data[ e.children ]; for ( uint32 i = 0; i < e.children; ++i ) { nmd_element_header element_header; source.read( &element_header, sizeof( element_header ), 1 ); NV_ASSERT( element_header.type == nmd_type::NODE, "NODE expected!" ); m_node_names.push_back( element_header.name ); uint16 ch_count = element_header.children; nmd_node_header node_header; source.read( &node_header, sizeof( node_header ), 1 ); m_node_array[i].parent_id = node_header.parent_id; m_node_array[i].transform = node_header.transform; m_node_array[i].data = nullptr; if ( ch_count > 0 ) { key_data* kdata = new key_data; m_node_array[i].data = kdata; for ( uint32 c = 0; c < ch_count; ++c ) { source.read( &element_header, sizeof( element_header ), 1 ); NV_ASSERT( element_header.type == nmd_type::KEY_CHANNEL, "CHANNEL expected!" ); nv::nmd_key_channel_header cheader; source.read( &cheader, sizeof( cheader ), 1 ); key_raw_channel* channel = key_raw_channel::create( cheader.format, cheader.count ); source.read( channel->data, channel->desc.size, channel->count ); kdata->add_channel( channel ); } } } m_node_data = new mesh_nodes_data( "animation", e.children, m_node_array, animation_header.frame_rate, animation_header.duration, animation_header.flat ); return true; } mesh_nodes_data* nv::nmd_loader::release_mesh_nodes_data() { if ( m_node_data ) { if ( m_strings ) { for ( uint32 i = 0; i < m_node_data->get_count(); ++i ) { m_node_array[i].name = m_strings->get( m_node_names[i] ); } } mesh_nodes_data* result = m_node_data; m_node_data = nullptr; m_node_array = nullptr; return result; } return nullptr; } // TEMPORARY nv::nmd_temp_model_data::nmd_temp_model_data( nmd_loader* loader ) { for ( unsigned m = 0; m < loader->get_mesh_count(); ++m ) { m_mesh_data.push_back(loader->release_mesh_data(m)); } m_node_data = loader->release_mesh_nodes_data(); } nv::nmd_temp_model_data::~nmd_temp_model_data() { for ( unsigned m = 0; m < m_mesh_data.size(); ++m ) { delete m_mesh_data[m]; } delete m_node_data; }