// Copyright (C) 2012-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/gfx/mesh_creator.hh" #include "nv/interface/data_channel_access.hh" #include "nv/core/logging.hh" struct nv_key_transform { nv::transform tform; }; void nv::mesh_nodes_creator::pre_transform_keys() { if ( m_data->m_flat ) return; merge_keys(); uint16 max_frames = 0; nv::vector< sint16 > ids; { // TODO: simplify this shit! // The complexity here is that we cannot pre-transform in any order // as the bones depend on previous bones, but ARE NOT IN ORDER // // Either rewrite this a lot nicer, or sort the bones on creation // by tree-order. ids.reserve( m_data->m_data.size() ); { nv::vector< sint16 > ids_next; ids_next.reserve( m_data->m_data.size() ); ids_next.push_back( -1 ); while ( !ids_next.empty() ) { sint16 pid = ids_next.back(); ids_next.pop_back(); for ( sint16 i = 0; i < sint16(m_data->m_data.size()); ++i ) if ( m_data->m_data[i]->get_parent_id() == pid ) { sint16* it = nv::find( ids.begin(), ids.end(), i ); if ( it == ids.end() ) { ids.push_back( i ); ids_next.push_back( i ); } } } } if ( ids.size() != m_data->m_data.size() ) { NV_LOG_WARNING( "Bad skeleton!" ); } } NV_LOG_DEBUG( "ID/PID" ); for ( auto id : ids ) { data_channel_set* keys = m_data->m_data[id]; sint16 parent_id = keys->get_parent_id(); NV_LOG_DEBUG( "Id : ", id, " PID", parent_id ); data_channel_set* pkeys = ( parent_id != -1 ? m_data->m_data[parent_id] : nullptr ); size_t count = ( keys ? keys->get_channel_size(0) : 0 ); size_t pcount = ( pkeys ? pkeys->get_channel_size(0) : 0 ); max_frames = nv::max( uint16( count ), max_frames ); if ( pkeys && pkeys->size() > 0 && keys && keys->size() > 0 ) { data_channel_access< nv_key_transform > channel_creator( keys, 0 ); nv_key_transform* channel = channel_creator.data(); const nv_key_transform* pchannel = pkeys->get_channel(0)->data_cast< nv_key_transform >(); for ( unsigned n = 0; n < count; ++n ) { channel[n].tform = pchannel[ nv::min( n, pcount-1 ) ].tform * channel[n].tform; } } } // DAE pre_transform hack if ( m_data->m_frame_rate == 1 ) { m_data->m_frame_rate = 32; m_data->m_frame_count = max_frames; } m_data->m_flat = true; } void nv::mesh_nodes_creator::merge_keys() { for ( size_t i = 0; i < m_data->size(); ++i ) { data_channel_set* old_keys = m_data->m_data[i]; if ( old_keys && old_keys->size() > 0 ) { size_t chan_count = old_keys->size(); if ( chan_count == 1 && old_keys->get_channel(0)->descriptor().size() == 1 && old_keys->get_channel(0)->descriptor()[0].etype == TRANSFORM ) continue; size_t max_keys = 0; for ( size_t c = 0; c < chan_count; ++c ) { max_keys = nv::max( max_keys, old_keys->get_channel(c)->size() ); } data_channel_set* new_keys = data_channel_set_creator::create_set( 1 ); data_channel_set_creator nk_access( new_keys ); nk_access.set_name( old_keys->get_name() ); nk_access.set_parent_id( old_keys->get_parent_id() ); nk_access.set_transform( old_keys->get_transform() ); data_channel_access< nv_key_transform > kt_channel( nk_access.add_channel( max_keys ) ); raw_channel_interpolator interpolator( old_keys ); data_descriptor final_key = interpolator.get_interpolation_key(); for ( unsigned n = 0; n < max_keys; ++n ) { float key[ 16 ]; float* pkey = key; for ( uint16 c = 0; c < chan_count; ++c ) { size_t idx = nv::min( old_keys->get_channel_size(c) - 1, n ); pkey += raw_channel_interpolator::get_raw( *old_keys->get_channel(c), idx, pkey ); } kt_channel.data()[n].tform = extract_key_raw< nv::transform >( final_key, key ); } delete old_keys; m_data->m_data[i] = new_keys; } } } void nv::mesh_nodes_creator::transform( float scale, const mat3& r33 ) { mat3 ri33 = math::inverse( r33 ); mat4 pre_transform ( scale * r33 ); mat4 post_transform( 1.f/scale * ri33 ); for ( auto node : m_data->m_data ) { node->m_transform = pre_transform * node->m_transform * post_transform; for ( size_t c = 0; c < node->size(); ++c ) { raw_data_channel_access channel( node, c ); size_t key_size = channel.element_size(); for ( size_t n = 0; n < channel.size(); ++n ) { transform_key_raw( node->get_channel( c )->descriptor(), channel.raw_data() + n * key_size, scale, r33, ri33 ); } } } } nv::mesh_data_creator::mesh_data_creator( data_channel_set* data ) : m_data( data ) { initialize(); } void nv::mesh_data_creator::transform( float scale, const mat3& r33 ) { vec3 vertex_offset = vec3(); mat3 vertex_transform = scale * r33; mat3 normal_transform = r33; for ( uint32 c = 0; c < m_data->size(); ++c ) { raw_data_channel_access channel( m_data, c ); const data_descriptor& desc = channel.descriptor(); uint8* raw_data = channel.raw_data(); uint32 vtx_size = desc.element_size(); int p_offset = -1; int n_offset = -1; int t_offset = -1; for ( const auto& cslot : desc ) switch ( cslot.vslot ) { case slot::POSITION : if ( cslot.etype == FLOAT_VECTOR_3 ) p_offset = int( cslot.offset ); break; case slot::NORMAL : if ( cslot.etype == FLOAT_VECTOR_3 ) n_offset = int( cslot.offset ); break; case slot::TANGENT : if ( cslot.etype == FLOAT_VECTOR_4 ) t_offset = int( cslot.offset ); break; default : break; } if ( p_offset != -1 ) for ( uint32 i = 0; i < channel.size(); i++) { vec3& p = *reinterpret_cast( raw_data + vtx_size*i + p_offset ); p = vertex_transform * p + vertex_offset; } if ( n_offset != -1 ) for ( uint32 i = 0; i < channel.size(); i++) { vec3& n = *reinterpret_cast( raw_data + vtx_size*i + n_offset ); n = math::normalize( normal_transform * n ); } if ( t_offset != -1 ) for ( uint32 i = 0; i < channel.size(); i++) { vec4& t = *reinterpret_cast(raw_data + vtx_size*i + t_offset ); t = vec4( math::normalize( normal_transform * vec3(t) ), t[3] ); } } } struct vertex_g { nv::vec4 tangent; }; void nv::mesh_data_creator::flip_normals() { if ( m_nrm_channel == nullptr ) return; NV_ASSERT( m_nrm_type == FLOAT_VECTOR_3, "Unknown normal vector type!" ); raw_data_channel_access channel( m_nrm_channel ); for ( uint32 i = 0; i < channel.size(); ++i ) { vec3& normal = *reinterpret_cast( channel.raw_data() + channel.element_size() * i + m_nrm_offset ); normal = -normal; } } void nv::mesh_data_creator::scale_texture( vec2 min, vec2 max ) { if ( m_tex_channel == nullptr ) return; NV_ASSERT( m_tex_type == FLOAT_VECTOR_2, "Unknown texcoord vector type!" ); raw_data_channel_access channel( m_tex_channel ); vec2 scale = max - min; for ( uint32 i = 0; i < channel.size(); ++i ) { vec2& tc = *reinterpret_cast( channel.raw_data() + channel.element_size() * i + m_tex_offset ); tc = min + tc * scale; } } void nv::mesh_data_creator::generate_tangents() { if ( m_tan_channel != nullptr ) return; if ( !m_pos_channel || !m_nrm_channel || !m_tex_channel ) return; if ( m_pos_channel->size() != m_nrm_channel->size() || m_pos_channel->size() % m_tex_channel->size() != 0 || ( m_idx_type != UINT && m_idx_type != USHORT && m_idx_type != NONE ) ) { return; } NV_ASSERT( m_pos_type == FLOAT_VECTOR_3, "Unsupported position vector type!" ); NV_ASSERT( m_nrm_type == FLOAT_VECTOR_3, "Unsupported normal vector type!" ); NV_ASSERT( m_tex_type == FLOAT_VECTOR_2, "Unknown texcoord vector type!" ); raw_data_channel g_channel = data_channel_creator::create< vertex_g >( m_pos_channel->size() ); vec4* tangents = &( data_channel_access< vertex_g >( &g_channel ).data()[0].tangent ); fill_n( tangents, m_pos_channel->size(), vec4() ); vec3* tangents2 = new vec3[ m_pos_channel->size() ]; uint32 tri_count = m_idx_channel ? m_idx_channel->size() / 3 : m_tex_channel->size() / 3; uint32 vtx_count = m_pos_channel->size(); uint32 sets = m_pos_channel->size() / m_tex_channel->size(); for ( unsigned int i = 0; i < tri_count; ++i ) { uint32 ti0 = 0; uint32 ti1 = 0; uint32 ti2 = 0; if ( m_idx_type == UINT ) { const uint32* idata = reinterpret_cast( m_idx_channel->raw_data() ); ti0 = idata[ i * 3 ]; ti1 = idata[ i * 3 + 1 ]; ti2 = idata[ i * 3 + 2 ]; } else if ( m_idx_type == USHORT ) { const uint16* idata = reinterpret_cast( m_idx_channel->raw_data() ); ti0 = idata[ i * 3 ]; ti1 = idata[ i * 3 + 1 ]; ti2 = idata[ i * 3 + 2 ]; } else // if ( m_idx_type == NONE ) { ti0 = i * 3; ti1 = i * 3 + 1; ti2 = i * 3 + 2; } vec2 w1 = *reinterpret_cast( m_tex_channel->raw_data() + m_tex_channel->element_size()*ti0 + m_tex_offset ); vec2 w2 = *reinterpret_cast( m_tex_channel->raw_data() + m_tex_channel->element_size()*ti1 + m_tex_offset ); vec2 w3 = *reinterpret_cast( m_tex_channel->raw_data() + m_tex_channel->element_size()*ti2 + m_tex_offset ); vec2 st1 = w3 - w1; vec2 st2 = w2 - w1; float stst = (st1.x * st2.y - st2.x * st1.y); float coef = ( stst != 0.0f ? 1.0f / stst : 0.0f ); for ( uint32 set = 0; set < sets; ++set ) { uint32 nti0 = m_tex_channel->size() * set + ti0; uint32 nti1 = m_tex_channel->size() * set + ti1; uint32 nti2 = m_tex_channel->size() * set + ti2; const vec3& v1 = *reinterpret_cast( m_pos_channel->raw_data() + m_pos_channel->element_size()*nti0 + m_pos_offset ); const vec3& v2 = *reinterpret_cast( m_pos_channel->raw_data() + m_pos_channel->element_size()*nti1 + m_pos_offset ); const vec3& v3 = *reinterpret_cast( m_pos_channel->raw_data() + m_pos_channel->element_size()*nti2 + m_pos_offset ); vec3 xyz1 = v3 - v1; vec3 xyz2 = v2 - v1; //vec3 normal = math::cross( xyz1, xyz2 ); // //vtcs[ ti0 ].normal += normal; //vtcs[ ti1 ].normal += normal; //vtcs[ ti2 ].normal += normal; vec3 tangent = (( xyz1 * st2.y ) - ( xyz2 * st1.y )) * coef; vec3 tangent2 = (( xyz2 * st1.x ) - ( xyz1 * st2.x )) * coef; tangents[nti0] = vec4( vec3( tangents[nti0] ) + tangent, 0 ); tangents[nti1] = vec4( vec3( tangents[nti1] ) + tangent, 0 ); tangents[nti2] = vec4( vec3( tangents[nti2] ) + tangent, 0 ); tangents2[nti0] += tangent2; tangents2[nti1] += tangent2; tangents2[nti2] += tangent2; } } for ( unsigned int i = 0; i < vtx_count; ++i ) { const vec3 n = *reinterpret_cast( m_nrm_channel->raw_data() + m_nrm_channel->element_size()*i + m_nrm_offset ); const vec3 t = vec3(tangents[i]); if ( ! ( t.x == 0.0f && t.y == 0.0f && t.z == 0.0f ) ) { tangents[i] = vec4( math::normalize(t - n * math::dot( n, t )), 0.0f ); tangents[i][3] = ( math::dot( math::cross(n, t), tangents2[i]) < 0.0f) ? -1.0f : 1.0f; } } delete[] tangents2; uint32 n_channel_index = m_data->get_channel_index( slot::NORMAL ); data_channel_set_creator( m_data ).set_channel( n_channel_index, merge_channels( *m_nrm_channel, g_channel ) ); initialize(); } void nv::mesh_data_creator::rotate_quadrant( uint8 rotation ) { if ( rotation % 4 == 0 ) return; NV_ASSERT( m_pos_type == FLOAT_VECTOR_3, "Unsupported position vector type!" ); NV_ASSERT( m_nrm_type == FLOAT_VECTOR_3, "Unsupported normal vector type!" ); NV_ASSERT( m_tan_type == FLOAT_VECTOR_4, "Unsupported tangent vector type!" ); float r11 = 0.f; float r12 = 0.f; float r21 = 0.f; float r22 = 0.f; switch ( rotation % 4 ) { case 1: r12 = -1.f; r21 = 1.f; break; case 2: r11 = -1.f; r22 = -1.f; break; case 3: r12 = 1.f; r21 = -1.f; break; default: break; } unsigned vtx_count = m_pos_channel->size(); uint8* pos_data = raw_data_channel_access( m_pos_channel ).raw_data(); uint8* nrm_data = raw_data_channel_access( m_nrm_channel ).raw_data(); uint8* tan_data = raw_data_channel_access( m_tan_channel ).raw_data(); for ( unsigned int i = 0; i < vtx_count; ++i ) { vec3& pos = *reinterpret_cast( pos_data + m_pos_channel->element_size() * i + m_pos_offset ); vec3& nrm = *reinterpret_cast( nrm_data + m_nrm_channel->element_size() * i + m_nrm_offset ); vec4& tan = *reinterpret_cast( tan_data + m_tan_channel->element_size() * i + m_tan_offset ); pos = vec3( pos.x * r11 + pos.z * r12, pos.y, pos.x * r21 + pos.z * r22 ); nrm = vec3( nrm.x * r11 + nrm.z * r12, nrm.y, nrm.x * r21 + nrm.z * r22 ); tan = vec4( tan.x * r11 + tan.z * r12, tan.y, tan.x * r21 + tan.z * r22, tan.w // make sure this is proper ); } } void nv::mesh_data_creator::mirror( bool x, bool z ) { if ( !x && !z ) return; NV_ASSERT( m_pos_type == FLOAT_VECTOR_3, "Unsupported position vector type!" ); NV_ASSERT( m_nrm_type == FLOAT_VECTOR_3, "Unsupported normal vector type!" ); NV_ASSERT( m_tan_type == FLOAT_VECTOR_4, "Unsupported tangent vector type!" ); float kx = x ? -1.0f : 1.0f; float kz = z ? -1.0f : 1.0f; unsigned vtx_count = m_pos_channel->size(); uint8* pos_data = raw_data_channel_access( m_pos_channel ).raw_data(); uint8* nrm_data = raw_data_channel_access( m_nrm_channel ).raw_data(); uint8* tan_data = raw_data_channel_access( m_tan_channel ).raw_data(); for ( unsigned int i = 0; i < vtx_count; ++i ) { vec3& pos = *reinterpret_cast( pos_data + m_pos_channel->element_size() * i + m_pos_offset ); vec3& nrm = *reinterpret_cast( nrm_data + m_nrm_channel->element_size() * i + m_nrm_offset ); vec4& tan = *reinterpret_cast( tan_data + m_tan_channel->element_size() * i + m_tan_offset ); pos = vec3( pos.x * kx, pos.y, pos.z * kz ); nrm = vec3( nrm.x * kx, nrm.y, nrm.z * kz ); tan = vec4( tan.x * kx, tan.y, tan.z * kz, tan.w * kx * kz// make sure this is proper ); } if ( !( x && z ) ) swap_culling(); } template < typename T > static inline void swap_culling_impl( nv::raw_data_channel* index_channel ) { nv::raw_data_channel_access ichannel( index_channel ); T* indices = reinterpret_cast( ichannel.raw_data() ); nv::uint32 count = index_channel->size() / 3; for ( nv::uint32 i = 0; i < count; ++i ) { nv::swap( indices[i * 3], indices[i * 3 + 1] ); } } void nv::mesh_data_creator::swap_culling() { NV_ASSERT( m_idx_channel, "Swap culling unsupported on non-indexed meshes!" ); NV_ASSERT( m_idx_channel->descriptor().size() == 1, "Malformed index channel!" ); NV_ASSERT( m_idx_channel->size() % 3 == 0, "Malformed index channel - not per GL_TRIANGLE LAYOUT?" ); if ( m_idx_channel->size() == 0 ) return; switch ( m_idx_type ) { case USHORT: swap_culling_impl< uint16 >( m_idx_channel ); break; case UINT: swap_culling_impl< uint16 >( m_idx_channel ); break; default: NV_ASSERT( false, "Swap culling supports only unsigned and unsigned short indices!" ); break; } } void nv::mesh_data_creator::translate( vec3 offset ) { if ( m_pos_channel == nullptr ) return; NV_ASSERT( m_pos_type == FLOAT_VECTOR_3, "Unsupported poosition vector type!" ); raw_data_channel_access channel( m_pos_channel ); for ( uint32 i = 0; i < channel.size(); ++i ) { vec3& p = *reinterpret_cast( channel.raw_data() + channel.element_size() * i + m_pos_offset ); p = p + offset; } } void nv::mesh_data_creator::initialize() { NV_ASSERT( m_data, "bad parameter!" ); m_pos_channel = nullptr; m_nrm_channel = nullptr; m_tan_channel = nullptr; m_tex_channel = nullptr; m_idx_channel = nullptr; m_pos_offset = -1; m_nrm_offset = -1; m_tan_offset = -1; m_tex_offset = -1; m_idx_offset = -1; m_pos_type = NONE; m_nrm_type = NONE; m_tan_type = NONE; m_tex_type = NONE; m_idx_type = NONE; for ( uint32 c = 0; c < m_data->size(); ++c ) { raw_data_channel* channel = data_channel_set_creator( m_data )[c]; for ( const auto& cslot : channel->descriptor() ) switch ( cslot.vslot ) { case slot::POSITION: m_pos_type = cslot.etype; m_pos_offset = int( cslot.offset ); m_pos_channel = channel; break; case slot::NORMAL: m_nrm_type = cslot.etype; m_nrm_offset = int( cslot.offset ); m_nrm_channel = channel; break; case slot::TANGENT: m_tan_type = cslot.etype; m_tan_offset = int( cslot.offset ); m_tan_channel = channel; break; case slot::TEXCOORD: m_tex_type = cslot.etype; m_tex_offset = int( cslot.offset ); m_tex_channel = channel; break; case slot::INDEX: m_idx_type = cslot.etype; m_idx_offset = int( cslot.offset ); m_idx_channel = channel; break; default: break; } } } nv::raw_data_channel nv::mesh_data_creator::merge_channels( const raw_data_channel& a, const raw_data_channel& b ) { NV_ASSERT( a.size() == b.size(), "merge_channel - bad channels!" ); data_descriptor desc = a.descriptor(); desc.append( b.descriptor() ); raw_data_channel result = data_channel_creator::create( desc, a.size() ); for ( uint32 i = 0; i < a.size(); ++i ) { raw_copy_n( a.raw_data() + i * a.element_size(), a.element_size(), raw_data_channel_access( &result ).raw_data() + i*desc.element_size() ); raw_copy_n( b.raw_data() + i * b.element_size(), b.element_size(), raw_data_channel_access( &result ).raw_data() + i*desc.element_size() + a.element_size() ); } initialize(); return result; } nv::raw_data_channel nv::mesh_data_creator::append_channels( const raw_data_channel& a, const raw_data_channel& b, uint32 frame_count ) { NV_ASSERT( a.descriptor() == b.descriptor(), "Merge - append not compatible format!" ); NV_ASSERT( a.size() % frame_count == 0, "Merge - append first mesh empty!" ); NV_ASSERT( b.size() % frame_count == 0, "Merge - append second mesh empty!" ); size_t vtx_size = a.element_size(); raw_data_channel result = data_channel_creator::create( a.descriptor(), a.size() + b.size() ); uint8* rdata = raw_data_channel_access( &result ).raw_data(); if ( frame_count == 1 ) { size_t a_size = vtx_size * a.size(); raw_copy_n( a.raw_data(), a_size, rdata ); raw_copy_n( b.raw_data(), vtx_size * b.size(), rdata + a_size ); } else { size_t frame_size_a = ( a.size() / frame_count ) * vtx_size; size_t frame_size_b = ( b.size() / frame_count ) * vtx_size; size_t pos_a = 0; size_t pos_b = 0; size_t pos = 0; for ( size_t i = 0; i < frame_count; ++i ) { raw_copy_n( a.raw_data() + pos_a, frame_size_a, rdata + pos ); raw_copy_n( b.raw_data() + pos_b, frame_size_b, rdata + pos + frame_size_a ); pos_a += frame_size_a; pos_b += frame_size_b; pos += frame_size_a + frame_size_b; } } initialize(); return result; } bool nv::mesh_data_creator::is_same_format( const data_channel_set* other ) { if ( m_data->size() != other->size() ) return false; for ( uint32 c = 0; c < m_data->size(); ++c ) { if ( m_data->get_channel(c)->descriptor() != other->get_channel(c)->descriptor() ) return false; } return true; } void nv::mesh_data_creator::merge( const data_channel_set* other ) { if ( !is_same_format( other ) ) return; int ch_pi = m_data->get_channel_index( slot::POSITION ); int ch_ti = m_data->get_channel_index( slot::TEXCOORD ); int och_pi = other->get_channel_index( slot::POSITION ); int och_ti = other->get_channel_index( slot::TEXCOORD ); if ( ch_pi == -1 || ch_ti == -1 ) return; size_t size = m_data->get_channel_size( unsigned(ch_ti) ); size_t osize = other->get_channel_size( unsigned(och_ti) ); size_t count = m_data->get_channel_size( unsigned(ch_pi) ); size_t ocount = other->get_channel_size( unsigned(och_pi) ); if ( count % size != 0 || ocount % osize != 0 ) return; if ( count / size != ocount / osize ) return; data_channel_set_creator data( m_data ); for ( uint32 c = 0; c < m_data->size(); ++c ) { const raw_data_channel* old = m_data->get_channel( c ); uint32 old_size = old->size(); data_descriptor old_desc = old->descriptor(); bool old_is_index = old_size > 0 && old_desc[0].vslot == slot::INDEX; size_t frame_count = ( old_is_index ? 1 : old_size / size ); data.set_channel( c, append_channels( *old, *other->get_channel(c), frame_count ) ); if ( old_is_index ) { switch ( old_desc[0].etype ) { case USHORT : { NV_ASSERT( size + osize < uint16(-1), "Index out of range!" ); raw_data_channel_access ic( data[c] ); uint16* indexes = reinterpret_cast( ic.raw_data() ); for ( uint16 i = uint16( old_size ); i < ic.size(); ++i ) indexes[i] += uint16( size ); } break; case UINT : { raw_data_channel_access ic( data[c] ); uint32* indexes = reinterpret_cast( ic.raw_data() ); for ( uint32 i = old_size; i < ic.size(); ++i ) indexes[i] += size; } break; default : NV_ASSERT( false, "Unsupported index type!" ); break; } } } initialize(); } void nv::mesh_creator::delete_mesh( uint32 index ) { if ( index < m_pack->get_count() ) { m_pack->m_meshes[index] = move( m_pack->m_meshes[m_pack->m_count - 1] ); m_pack->m_count--; } }