// 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 /** * @file device.hh * @author Kornel Kisielewicz epyon@chaosforge.org * @brief Device class */ #ifndef NV_DEVICE_HH #define NV_DEVICE_HH #include #include #include #include #include #include #include namespace nv { class window; enum texture_slot { TEX_DIFFUSE = 0, TEX_SPECULAR = 1, TEX_NORMAL = 2, TEXTURE_0 = 0, TEXTURE_1 = 1, TEXTURE_2 = 2, TEXTURE_3 = 3, TEXTURE_4 = 4, TEXTURE_5 = 5, TEXTURE_6 = 6, TEXTURE_7 = 7, }; struct attribute { string name; int location; datatype type; int length; }; typedef std::unordered_map< string, attribute > attribute_map; struct texture_tag {}; struct vertex_array_tag {}; struct buffer_tag {}; struct program_tag {}; typedef handle< uint32, 16, 16, buffer_tag > buffer; typedef handle< uint32, 16, 16, texture_tag > texture; typedef handle< uint32, 16, 16, vertex_array_tag > vertex_array; typedef handle< uint32, 16, 16, program_tag > program; struct sampler { enum filter { LINEAR, NEAREST, NEAREST_MIPMAP_NEAREST, LINEAR_MIPMAP_NEAREST, NEAREST_MIPMAP_LINEAR, LINEAR_MIPMAP_LINEAR }; enum wrap { CLAMP_TO_EDGE, CLAMP_TO_BORDER, MIRRORED_REPEAT, REPEAT }; filter filter_min; filter filter_max; wrap wrap_s; wrap wrap_t; sampler() : filter_min( LINEAR ), filter_max( LINEAR ), wrap_s( REPEAT ), wrap_t( REPEAT ) {} sampler( filter min, filter max, wrap s, wrap t ) : filter_min( min ), filter_max( max ), wrap_s( s ), wrap_t( t ) {} sampler( filter f, wrap w ) : filter_min( f ), filter_max( f ), wrap_s( w ), wrap_t( w ) {} }; struct buffer_info { buffer_type type; buffer_hint hint; size_t size; }; struct texture_info { ivec2 size; image_format format; sampler sampler; }; struct vertex_buffer_attribute { buffer vbuffer; datatype dtype; size_t components; size_t offset; size_t stride; slot location; bool owner; }; struct vertex_array_info { static const int MAX_ATTRIBUTES = 24; uint32 count; buffer index; bool index_owner; datatype index_type; vertex_buffer_attribute attr[MAX_ATTRIBUTES]; }; struct program_info { attribute_map m_attribute_map; uniform_map m_uniform_map; engine_uniform_list m_engine_uniforms; }; class device { friend class context; public: device() { initialize_engine_uniforms(); } virtual window* create_window( uint16 width, uint16 height, bool fullscreen ) = 0; virtual window* adopt_window( void* sys_w_handle, void* sys_dc ) = 0; virtual program create_program( const string& vs_source, const string& fs_source ) = 0; virtual buffer create_buffer( buffer_type type, buffer_hint hint, size_t size, const void* source = nullptr ) = 0; virtual image_data* create_image_data( const std::string& filename ) = 0; // temporary virtual texture create_texture( ivec2 size, image_format aformat, sampler asampler, void* data = nullptr ) = 0; virtual vertex_array create_vertex_array() = 0; virtual void release( texture ) = 0; virtual void release( buffer ) = 0; virtual void release( vertex_array ) = 0; virtual void release( program ) = 0; virtual const texture_info* get_texture_info( texture ) const = 0; virtual const buffer_info* get_buffer_info( buffer ) const = 0; virtual const vertex_array_info* get_vertex_array_info( vertex_array ) const = 0; virtual uint32 get_ticks() = 0; virtual void delay( uint32 ms ) = 0; virtual texture create_texture( image_data* data, sampler asampler ) { return create_texture( data->get_size(), data->get_format(), asampler, (void*)data->get_data() ); } template < typename VTX, slot SLOT > void add_vertex_buffer_impl( vertex_array, buffer, const std::false_type& ) { } template < typename VTX, slot SLOT > void add_vertex_buffer_impl( vertex_array va, buffer vb, const std::true_type& ) { typedef vertex_slot_info< VTX, SLOT > vinfo; typedef datatype_traits< typename vinfo::value_type > dt_traits; add_vertex_buffer( va, SLOT, vb, type_to_enum< dt_traits::base_type >::type, dt_traits::size, vinfo::offset, sizeof( VTX ), false ); } template < typename VTX, slot SLOT > void add_vertex_buffer( vertex_array va, buffer vb ) { add_vertex_buffer_impl< VTX, SLOT >( va, vb, std::integral_constant< bool, vertex_has_slot< VTX, SLOT >::value >() ); } template < typename VTX > void add_vertex_buffers( vertex_array va, buffer vb ) { add_vertex_buffer< VTX, slot::POSITION > ( va, vb ); add_vertex_buffer< VTX, slot::TEXCOORD > ( va, vb ); add_vertex_buffer< VTX, slot::NORMAL > ( va, vb ); add_vertex_buffer< VTX, slot::TANGENT > ( va, vb ); add_vertex_buffer< VTX, slot::BONEINDEX > ( va, vb ); add_vertex_buffer< VTX, slot::BONEWEIGHT >( va, vb ); add_vertex_buffer< VTX, slot::COLOR > ( va, vb ); } void add_vertex_buffers( vertex_array va, buffer buf, const mesh_raw_channel* channel ) { for ( uint32 s = 0; s < channel->desc.count; ++s ) { const vertex_descriptor_slot& slot = channel->desc.slots[s]; const datatype_info& info = get_datatype_info(slot.etype); add_vertex_buffer( va, slot.vslot, buf, info.base , info.elements, slot.offset, channel->desc.size, false ); } } template < typename VTX > vertex_array create_vertex_array( const VTX* v, size_t count, buffer_hint hint ) { // TODO: vb will not be owned or freed! vertex_array va = create_vertex_array(); buffer vb = create_buffer( VERTEX_BUFFER, hint, count * sizeof( VTX ), v ); add_vertex_buffers< VTX >( va, vb ); return va; } template < typename VTX > vertex_array create_vertex_array( const std::vector< VTX >& data, buffer_hint hint ) { return create_vertex_array( data.data(), data.size(), hint ); } template < typename VTX, typename IDX > vertex_array create_vertex_array( const VTX* v, size_t vcount, const IDX* i, size_t icount, buffer_hint hint ) { vertex_array va = create_vertex_array( v, vcount, hint ); buffer ib = create_buffer( INDEX_BUFFER, hint, icount * sizeof( IDX ), i ); va->set_index_buffer( ib, type_to_enum< IDX >::type, true ); return va; } template < typename VTX, typename IDX > vertex_array create_vertex_array( const std::vector< VTX >& data, const std::vector< IDX >& idata, buffer_hint hint ) { return create_vertex_array( data.data(), data.size(), idata.data(), idata.size(), hint ); } void replace_vertex_buffer( vertex_array va, buffer vb, bool owner ) { vertex_array_info* info = get_vertex_array_info_mutable( va ); if ( info ) { for ( uint32 i = 0; i < info->count; ++i ) { vertex_buffer_attribute& vba = info->attr[i]; if ( vba.owner ) release( vba.vbuffer ); vba.vbuffer = vb; vba.owner = owner; } } } void replace_vertex_buffer( vertex_array va, buffer vb, slot location, bool owner ) { vertex_array_info* info = get_vertex_array_info_mutable( va ); if ( info ) { for ( uint32 i = 0; i < info->count; ++i ) { if ( info->attr[i].location == location ) { vertex_buffer_attribute& vba = info->attr[i]; if ( vba.owner ) release( vba.vbuffer ); vba.vbuffer = vb; vba.owner = owner; } } } } void update_attribute_offset( vertex_array va, slot location, size_t offset ) { vertex_array_info* info = get_vertex_array_info_mutable( va ); if ( info ) { for ( uint32 i = 0; i < info->count; ++i ) { if ( info->attr[i].location == location ) { info->attr[i].offset = offset; } } } } void set_index_buffer( vertex_array va, buffer b, datatype datatype, bool owner ) { vertex_array_info* info = get_vertex_array_info_mutable( va ); if ( info ) { if ( get_buffer_info( b )->type == INDEX_BUFFER ) { if (info->index.is_valid() && info->index_owner) release( info->index ); info->index = b; info->index_owner = owner; info->index_type = datatype; } } } virtual void add_vertex_buffer( vertex_array va, slot location, buffer buf, datatype datatype, size_t components, size_t offset = 0, size_t stride = 0, bool owner = true ) { vertex_array_info* info = get_vertex_array_info_mutable( va ); if ( info ) { NV_ASSERT( info->count < vertex_array_info::MAX_ATTRIBUTES, "MAX_ATTRIBUTES reached!" ); vertex_buffer_attribute& p = info->attr[ info->count ]; p.vbuffer = buf; p.dtype = datatype; p.components = components; p.offset = offset; p.stride = stride; p.owner = owner; p.location = location; info->count++; } } buffer find_buffer( vertex_array va, slot location ) { const vertex_array_info* info = get_vertex_array_info( va ); if ( info ) { for ( uint32 i = 0; i < info->count; ++i ) { if ( info->attr[i].location == location ) { return info->attr[i].vbuffer; } } } return buffer(); } // TODO: HINTS ARE DIFFERENT! vertex_array create_vertex_array( const mesh_data* data, buffer_hint hint ) { vertex_array va = create_vertex_array(); const std::vector< mesh_raw_channel* >& channels = data->get_raw_channels(); for ( uint32 ch = 0; ch < channels.size(); ++ch ) { const mesh_raw_channel* channel = channels[ch]; if ( channel->count > 0 ) { buffer_type type = channel->get_buffer_type(); buffer b = create_buffer( type, hint, channel->size(), channel->data ); // TODO: no if switch if ( type == INDEX_BUFFER ) { set_index_buffer( va, b, channel->desc.slots[0].etype, true ); } else { add_vertex_buffers( va, b, channel ); } } } return va; } int try_get_attribute_location( program p, const string& name ) const { return get_attribute_location( p, name, false ); } virtual int get_attribute_location( program p, const string& name, bool fatal = true ) const = 0; template < typename T > void set_uniform_array( program p, const string& name, const T* value, uint32 count, bool fatal = true ) { uniform_base* base = get_uniform( p, name, fatal ); if ( base != nullptr ) { if ( base->type_check( type_to_enum::type ) ) { // TODO: nicer check NV_ASSERT( (int)count <= base->get_length(), "LENGTH CHECK FAIL" ); ((uniform*)( base ))->set_value( value, count ); } } } template < typename T > void set_uniform_array( const string& name, const std::vector& value ) { set_uniform_array( program p, name, (const T*)value.data(), value.size() ); } template < typename T > void set_opt_uniform_array( program p, const string& name, const T* value, uint32 count ) { set_uniform_array( p, name, value, count, false ); } template < typename T > void set_opt_uniform_array( program p, const string& name, const std::vector& value ) { set_uniform_array( p, name, (const T*)value.data(), value.size(), false ); } template < typename T > void set_uniform( program p, const string& name, const T& value, bool fatal = true ) { uniform_base* base = get_uniform( p, name, fatal ); if ( base != nullptr ) { if ( base->type_check( type_to_enum::type ) ) { ((uniform*)( base ))->set_value( value ); } } } template < typename T > void set_opt_uniform( program p, const string& name, const T& value ) { set_uniform( p, name, value, false ); } virtual ~device() { destroy_engine_uniforms(); } // This is done this way to avoid compilation unit creation static engine_uniform_factory_map& get_uniform_factory() { static engine_uniform_factory_map s_engine_uniform_factory_map; return s_engine_uniform_factory_map; } // This is done this way to avoid compilation unit creation static engine_link_uniform_factory_map& get_link_uniform_factory() { static engine_link_uniform_factory_map s_engine_link_uniform_factory_map; return s_engine_link_uniform_factory_map; } virtual void prepare_program( program p ) = 0; protected: virtual uniform_base* get_uniform( program p, const string& name, bool fatal = true ) const = 0; // TODO: remove virtual vertex_array_info* get_vertex_array_info_mutable( vertex_array ) = 0; void initialize_engine_uniforms() { engine_uniform_factory_map& factory_map = get_uniform_factory(); factory_map[ "nv_m_view" ] = new engine_uniform_factory< engine_uniform_m_view >(); factory_map[ "nv_m_view_inv" ] = new engine_uniform_factory< engine_uniform_m_view_inv >(); factory_map[ "nv_m_model" ] = new engine_uniform_factory< engine_uniform_m_model >(); factory_map[ "nv_m_model_inv" ] = new engine_uniform_factory< engine_uniform_m_model_inv >(); factory_map[ "nv_m_modelview" ] = new engine_uniform_factory< engine_uniform_m_modelview >(); factory_map[ "nv_m_projection" ] = new engine_uniform_factory< engine_uniform_m_projection >(); factory_map[ "nv_m_normal" ] = new engine_uniform_factory< engine_uniform_m_normal >(); factory_map[ "nv_m_mvp" ] = new engine_uniform_factory< engine_uniform_m_mvp >(); factory_map[ "nv_v_camera_position" ] = new engine_uniform_factory< engine_uniform_v_camera_position >(); factory_map[ "nv_v_camera_direction" ] = new engine_uniform_factory< engine_uniform_v_camera_direction >(); engine_link_uniform_factory_map& factory_link_map = get_link_uniform_factory(); factory_link_map[ "nv_texture_0" ] = new engine_link_uniform_int<0>(); factory_link_map[ "nv_texture_1" ] = new engine_link_uniform_int<1>(); factory_link_map[ "nv_texture_2" ] = new engine_link_uniform_int<2>(); factory_link_map[ "nv_texture_3" ] = new engine_link_uniform_int<3>(); factory_link_map[ "nv_texture_4" ] = new engine_link_uniform_int<4>(); factory_link_map[ "nv_texture_5" ] = new engine_link_uniform_int<5>(); factory_link_map[ "nv_texture_6" ] = new engine_link_uniform_int<6>(); factory_link_map[ "nv_texture_7" ] = new engine_link_uniform_int<7>(); factory_link_map[ "nv_t_diffuse" ] = new engine_link_uniform_int<0>(); factory_link_map[ "nv_t_specular"] = new engine_link_uniform_int<1>(); factory_link_map[ "nv_t_normal" ] = new engine_link_uniform_int<2>(); } void destroy_engine_uniforms() { for ( auto& i : get_uniform_factory() ) delete i.second; for ( auto& i : get_link_uniform_factory() ) delete i.second; get_uniform_factory().clear(); get_link_uniform_factory().clear(); } }; } // namespace nv #endif // NV_DEVICE_HH