// Copyright (C) 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/rocket/rocket_interface.hh" #include #include #include #include #include class rocket_c_file_interface : public Rocket::Core::FileInterface { public: rocket_c_file_interface( const Rocket::Core::String& root ) : root( root ) {} virtual ~rocket_c_file_interface() {} virtual Rocket::Core::FileHandle Open( const Rocket::Core::String& path ); virtual void Close( Rocket::Core::FileHandle file ); virtual size_t Read( void* buffer, size_t size, Rocket::Core::FileHandle file ); virtual bool Seek( Rocket::Core::FileHandle file, long offset, int origin ); virtual size_t Tell( Rocket::Core::FileHandle file ); private: Rocket::Core::String root; }; class rocket_sdl2_system_interface : public Rocket::Core::SystemInterface { public: float GetElapsedTime(); bool LogMessage( Rocket::Core::Log::Type type, const Rocket::Core::String& message ); }; class rocket_sdl2_render_interface : public Rocket::Core::RenderInterface { public: rocket_sdl2_render_interface( nv::context* context ); /// Called by Rocket when it wants to render geometry that it does not wish to optimise. virtual void RenderGeometry( Rocket::Core::Vertex* vertices, int num_vertices, int* indices, int num_indices, Rocket::Core::TextureHandle texture, const Rocket::Core::Vector2f& translation ); /// Called by Rocket when it wants to enable or disable scissoring to clip content. virtual void EnableScissorRegion( bool enable ); /// Called by Rocket when it wants to change the scissor region. virtual void SetScissorRegion( int x, int y, int width, int height ); /// Called by Rocket when a texture is required by the library. virtual bool LoadTexture( Rocket::Core::TextureHandle& texture_handle, Rocket::Core::Vector2i& texture_dimensions, const Rocket::Core::String& source ); /// Called by Rocket when a texture is required to be built from an internally-generated sequence of pixels. virtual bool GenerateTexture( Rocket::Core::TextureHandle& texture_handle, const Rocket::Core::byte* source, const Rocket::Core::Vector2i& source_dimensions ); /// Called by Rocket when a loaded texture is no longer required. virtual void ReleaseTexture( Rocket::Core::TextureHandle texture_handle ); private: nv::device* m_device; nv::context* m_context; nv::program m_program; nv::scene_state m_state; nv::render_state m_rstate; }; static const char *rocket_vertex_shader = R"( #version 120 attribute vec2 nv_position; attribute vec2 nv_texcoord; attribute vec4 nv_color; varying vec4 v_color; varying vec2 v_texcoord; uniform mat4 nv_m_projection; uniform mat4 nv_m_model; uniform vec2 texsize; void main(void) { gl_Position = nv_m_projection * nv_m_model * vec4(nv_position.x, nv_position.y, 0.0, 1.0); v_texcoord = nv_texcoord; // / texsize; v_color = nv_color / 256; } )"; static const char *rocket_fragment_shader = R"( #version 120 varying vec4 v_color; varying vec2 v_texcoord; uniform sampler2D nv_t_diffuse; void main(void) { vec4 tex_color = texture2D( nv_t_diffuse, v_texcoord ); gl_FragColor = tex_color * v_color; } )"; Rocket::Core::FileHandle rocket_c_file_interface::Open( const Rocket::Core::String& path ) { // Attempt to open the file relative to the application's root. FILE* fp = fopen( ( root + path ).CString(), "rb" ); if ( fp != NULL ) return ( Rocket::Core::FileHandle ) fp; // Attempt to open the file relative to the current working directory. fp = fopen( path.CString(), "rb" ); return ( Rocket::Core::FileHandle ) fp; } void rocket_c_file_interface::Close( Rocket::Core::FileHandle file ) { fclose( (FILE*)file ); } size_t rocket_c_file_interface::Read( void* buffer, size_t size, Rocket::Core::FileHandle file ) { return fread( buffer, 1, size, (FILE*)file ); } bool rocket_c_file_interface::Seek( Rocket::Core::FileHandle file, long offset, int origin ) { return fseek( (FILE*)file, offset, origin ) == 0; } size_t rocket_c_file_interface::Tell( Rocket::Core::FileHandle file ) { return ftell( (FILE*)file ); } float rocket_sdl2_system_interface::GetElapsedTime() { return nv::get_ticks() / 1000; } bool rocket_sdl2_system_interface::LogMessage( Rocket::Core::Log::Type type, const Rocket::Core::String& message ) { switch ( type ) { case Rocket::Core::Log::LT_ALWAYS: NV_LOG( nv::LOG_NOTICE, message.CString() ); break; case Rocket::Core::Log::LT_ERROR: NV_LOG( nv::LOG_ERROR, message.CString() ); break; case Rocket::Core::Log::LT_ASSERT: NV_LOG( nv::LOG_CRITICAL, message.CString() ); break; case Rocket::Core::Log::LT_WARNING: NV_LOG( nv::LOG_WARNING, message.CString() ); break; case Rocket::Core::Log::LT_INFO: NV_LOG( nv::LOG_INFO, message.CString() ); break; case Rocket::Core::Log::LT_DEBUG: NV_LOG( nv::LOG_DEBUG, message.CString() ); break; case Rocket::Core::Log::LT_MAX: break; }; return true; } struct rocket_vertex { nv::vec2 position; nv::u8vec4 color; nv::vec2 texcoord; }; union texture_handle_convert { Rocket::Core::TextureHandle th; struct { nv::uint16 index; nv::uint16 counter; }; }; rocket_sdl2_render_interface::rocket_sdl2_render_interface( nv::context* context ) { m_context = context; m_device = m_context->get_device(); m_rstate.depth_test.enabled = false; m_rstate.culling.enabled = false; m_rstate.blending.enabled = true; m_rstate.blending.src_rgb_factor = nv::blending::SRC_ALPHA; m_rstate.blending.dst_rgb_factor = nv::blending::ONE_MINUS_SRC_ALPHA; m_rstate.blending.src_alpha_factor = nv::blending::SRC_ALPHA; m_rstate.blending.dst_alpha_factor = nv::blending::ONE_MINUS_SRC_ALPHA; m_program = m_device->create_program( rocket_vertex_shader, rocket_fragment_shader ); m_state.get_camera().set_ortho( 0.0f, float( 1024 ), float( 728 ), 0.0f ); } void rocket_sdl2_render_interface::EnableScissorRegion( bool enable ) { m_rstate.scissor_test.enabled = enable; } void rocket_sdl2_render_interface::SetScissorRegion( int x, int y, int width, int height ) { m_rstate.scissor_test.pos.x = x; m_rstate.scissor_test.pos.y = y; m_rstate.scissor_test.dim.x = width; m_rstate.scissor_test.pos.y = height; } bool rocket_sdl2_render_interface::LoadTexture( Rocket::Core::TextureHandle& texture_handle, Rocket::Core::Vector2i& texture_dimensions, const Rocket::Core::String& source ) { Rocket::Core::FileInterface* file_interface = Rocket::Core::GetFileInterface(); Rocket::Core::FileHandle file_handle = file_interface->Open( source ); if ( !file_handle ) return false; file_interface->Seek( file_handle, 0, SEEK_END ); size_t buffer_size = file_interface->Tell( file_handle ); file_interface->Seek( file_handle, 0, SEEK_SET ); nv::uint8* buffer = new nv::uint8[buffer_size]; file_interface->Read( buffer, buffer_size, file_handle ); file_interface->Close( file_handle ); Rocket::Core::String ext = source.Substring( source.Length() - 3, 3 ).ToLower(); nv::sampler sampler( nv::sampler::LINEAR, nv::sampler::REPEAT ); nv::image_data* data = m_device->create_image_data( buffer, buffer_size ); nv::image_format iformat( nv::RGBA, nv::UBYTE ); if ( ext == "tga" ) iformat.format = nv::BGRA; nv::texture tex = m_device->create_texture( data->get_size(), iformat, sampler, (void*)data->get_data() ); texture_dimensions.x = data->get_size().x; texture_dimensions.y = data->get_size().y; delete data; typedef nv::handle_operator< nv::texture > op; texture_handle_convert thc; thc.index = op::get_index( tex ); thc.counter = op::get_counter( tex ); texture_handle = thc.th; NV_LOG( nv::LOG_DEBUG, "load " << source.CString() << " as " << thc.index << "," << thc.counter ); return true; } bool rocket_sdl2_render_interface::GenerateTexture( Rocket::Core::TextureHandle& texture_handle, const Rocket::Core::byte* source, const Rocket::Core::Vector2i& source_dimensions ) { nv::sampler sampler( nv::sampler::LINEAR, nv::sampler::REPEAT ); nv::texture tex = m_device->create_texture( nv::ivec2( source_dimensions.x, source_dimensions.y ), nv::image_format( nv::RGBA, nv::UBYTE ), sampler, (void*)source ); typedef nv::handle_operator< nv::texture > op; texture_handle_convert thc; thc.index = op::get_index( tex ); thc.counter = op::get_counter( tex ); texture_handle = thc.th; return true; } void rocket_sdl2_render_interface::ReleaseTexture( Rocket::Core::TextureHandle texture_handle ) { texture_handle_convert thc; thc.th = texture_handle; typedef nv::handle_operator< nv::texture > op; m_device->release( op::create( thc.index, thc.counter ) ); } void rocket_sdl2_render_interface::RenderGeometry( Rocket::Core::Vertex* vertices, int num_vertices, int* indices, int num_indices, Rocket::Core::TextureHandle texture, const Rocket::Core::Vector2f& translation ) { const nv::texture_info* info = nullptr; if ( texture != 0 ) { texture_handle_convert thc; thc.th = texture; typedef nv::handle_operator< nv::texture > op; nv::texture tex( op::create( thc.index, thc.counter ) ); m_context->bind( tex, nv::TEX_DIFFUSE ); info = m_device->get_texture_info( tex ); } else { m_context->bind( nv::texture(), nv::TEX_DIFFUSE ); return; } rocket_vertex* rv = (rocket_vertex*)vertices; nv::vertex_array m_va = m_context->create_vertex_array( rv, num_vertices, (unsigned*)indices, num_indices, nv::STATIC_DRAW ); //if ( info ) m_device->set_uniform( m_program, "texsize", nv::vec2( info->size ) ); m_state.set_model( glm::translate( glm::mat4(), nv::vec3( translation.x, translation.y, 0.0f ) ) ); m_context->draw( nv::TRIANGLES, m_rstate, m_state, m_program, m_va, num_indices ); m_context->release( m_va ); } /* int RocketSDL2SystemInterface::TranslateMouseButton( Uint8 button ) { switch ( button ) { case SDL_BUTTON_LEFT: return 0; case SDL_BUTTON_RIGHT: return 1; case SDL_BUTTON_MIDDLE: return 2; default: return 3; } } int RocketSDL2SystemInterface::GetKeyModifiers() { SDL_Keymod sdlMods = SDL_GetModState(); int retval = 0; if ( sdlMods & KMOD_CTRL ) retval |= Rocket::Core::Input::KM_CTRL; if ( sdlMods & KMOD_SHIFT ) retval |= Rocket::Core::Input::KM_SHIFT; if ( sdlMods & KMOD_ALT ) retval |= Rocket::Core::Input::KM_ALT; return retval; } */ int rocket_get_key_modifiers() { return 0; } int rocket_get_mouse_buttons( const nv::mouse_button_event& button ) { return 0; } Rocket::Core::Input::KeyIdentifier rocket_translate_key( const nv::key_event& key ) { using namespace Rocket::Core::Input; switch ( key.code ) { case nv::KEY_NONE : return KI_UNKNOWN; case nv::KEY_SPACE : return KI_SPACE; case nv::KEY_0: return KI_0; case nv::KEY_1: return KI_1; case nv::KEY_2: return KI_2; case nv::KEY_3: return KI_3; case nv::KEY_4: return KI_4; case nv::KEY_5: return KI_5; case nv::KEY_6: return KI_6; case nv::KEY_7: return KI_7; case nv::KEY_8: return KI_8; case nv::KEY_9: return KI_9; case nv::KEY_A: return KI_A; case nv::KEY_B: return KI_B; case nv::KEY_C: return KI_C; case nv::KEY_D: return KI_D; case nv::KEY_E: return KI_E; case nv::KEY_F: return KI_F; case nv::KEY_G: return KI_G; case nv::KEY_H: return KI_H; case nv::KEY_I: return KI_I; case nv::KEY_J: return KI_J; case nv::KEY_K: return KI_K; case nv::KEY_L: return KI_L; case nv::KEY_M: return KI_M; case nv::KEY_N: return KI_N; case nv::KEY_O: return KI_O; case nv::KEY_P: return KI_P; case nv::KEY_Q: return KI_Q; case nv::KEY_R: return KI_R; case nv::KEY_S: return KI_S; case nv::KEY_T: return KI_T; case nv::KEY_U: return KI_U; case nv::KEY_V: return KI_V; case nv::KEY_W: return KI_W; case nv::KEY_X: return KI_X; case nv::KEY_Y: return KI_Y; case nv::KEY_Z: return KI_Z; case nv::KEY_SCOLON:return KI_OEM_1; // case nv::KEY_PLUS:return KI_OEM_PLUS; case nv::KEY_COMMA:return KI_OEM_COMMA; case nv::KEY_MINUS:return KI_OEM_MINUS; case nv::KEY_PERIOD:return KI_OEM_PERIOD; case nv::KEY_SLASH:return KI_OEM_2; case nv::KEY_BQUOTE:return KI_OEM_3; case nv::KEY_LBRACKET:return KI_OEM_4; case nv::KEY_BSLASH:return KI_OEM_5; case nv::KEY_RBRACKET:return KI_OEM_6; // case nv::KEY_QUOTEDBL:return KI_OEM_7; // case nv::KEY_KP_0:return KI_NUMPAD0; // case nv::KEY_KP_1:return KI_NUMPAD1; // case nv::KEY_KP_2:return KI_NUMPAD2; // case nv::KEY_KP_3:return KI_NUMPAD3; // case nv::KEY_KP_4:return KI_NUMPAD4; // case nv::KEY_KP_5:return KI_NUMPAD5; // case nv::KEY_KP_6:return KI_NUMPAD6; // case nv::KEY_KP_7:return KI_NUMPAD7; // case nv::KEY_KP_8:return KI_NUMPAD8; // case nv::KEY_KP_9:return KI_NUMPAD9; // case nv::KEY_KP_ENTER:return KI_NUMPADENTER; // case nv::KEY_KP_MULTIPLY:return KI_MULTIPLY; // case nv::KEY_KP_PLUS:return KI_ADD; // case nv::KEY_KP_MINUS: return KI_SUBTRACT; // case nv::KEY_KP_PERIOD: return KI_DECIMAL; // case nv::KEY_KP_DIVIDE: return KI_DIVIDE; // case nv::KEY_KP_EQUALS: return KI_OEM_NEC_EQUAL; case nv::KEY_BACK : return KI_BACK; case nv::KEY_TAB : return KI_TAB; // case nv::KEY_CLEAR : return KI_CLEAR; case nv::KEY_ENTER : return KI_RETURN; // case nv::KEY_PAUSE : return KI_PAUSE; // case nv::KEY_CAPSLOCK: return KI_CAPITAL; case nv::KEY_PGUP : return KI_PRIOR; case nv::KEY_PGDOWN: return KI_NEXT; case nv::KEY_END : return KI_END; case nv::KEY_HOME : return KI_HOME; case nv::KEY_LEFT : return KI_LEFT; case nv::KEY_UP : return KI_UP; case nv::KEY_RIGHT : return KI_RIGHT; case nv::KEY_DOWN : return KI_DOWN; case nv::KEY_INSERT: return KI_INSERT; case nv::KEY_DELETE: return KI_DELETE; case nv::KEY_F1 : return KI_F1; case nv::KEY_F2 : return KI_F2; case nv::KEY_F3 : return KI_F3; case nv::KEY_F4 : return KI_F4; case nv::KEY_F5 : return KI_F5; case nv::KEY_F6 : return KI_F6; case nv::KEY_F7 : return KI_F7; case nv::KEY_F8 : return KI_F8; case nv::KEY_F9 : return KI_F9; case nv::KEY_F10 : return KI_F10; case nv::KEY_F11 : return KI_F11; case nv::KEY_F12 : return KI_F12; // case nv::KEY_F13 : return KI_F13; // case nv::KEY_F14 : return KI_F14; // case nv::KEY_F15 : return KI_F15; // case nv::KEY_NUMLOCKCLEAR : return KI_NUMLOCK; // case nv::KEY_SCROLLLOCK : return KI_SCROLL; // case nv::KEY_LSHIFT : return KI_LSHIFT; // case nv::KEY_RSHIFT : return KI_RSHIFT; // case nv::KEY_LCTRL : return KI_LCONTROL; // case nv::KEY_RCTRL : return KI_RCONTROL; // case nv::KEY_LALT : return KI_LMENU; // case nv::KEY_RALT : return KI_RMENU; // case nv::KEY_LGUI : return KI_LMETA; // case nv::KEY_RGUI : return KI_RMETA; // case nv::KEY_LSUPER : return KI_LWIN; // case nv::KEY_RSUPER : return KI_RWIN; default: return KI_UNKNOWN; } return KI_UNKNOWN; } static rocket_sdl2_render_interface* s_render_interface = nullptr; static rocket_sdl2_system_interface* s_system_interface = nullptr; static rocket_c_file_interface* s_file_interface = nullptr; bool nv::rocket_event( void* context, nv::io_event& event ) { Rocket::Core::Context *Context = ( Rocket::Core::Context * )context; switch ( event.type ) { case nv::EV_MOUSE_MOVE : Context->ProcessMouseMove( event.mmove.x, event.mmove.y, rocket_get_key_modifiers() ); break; case nv::EV_MOUSE_BUTTON : if ( event.mbutton.pressed ) Context->ProcessMouseButtonDown( rocket_get_mouse_buttons( event.mbutton ), rocket_get_key_modifiers() ); else Context->ProcessMouseButtonUp( rocket_get_mouse_buttons( event.mbutton ), rocket_get_key_modifiers() ); break; case nv::EV_MOUSE_WHEEL: return Context->ProcessMouseWheel( event.mwheel.y, rocket_get_key_modifiers() ); case nv::EV_KEY: if (event.key.pressed) { if ( event.key.code == KEY_BQUOTE ) { Rocket::Debugger::SetVisible( !Rocket::Debugger::IsVisible() ); return true; } return Context->ProcessKeyDown( rocket_translate_key( event.key ) , rocket_get_key_modifiers() ); } break; default: break; } return false; } bool nv::rocket_initialize( nv::window* w, const char* assets_root ) { s_render_interface = new rocket_sdl2_render_interface( w->get_context() ); s_system_interface = new rocket_sdl2_system_interface(); s_file_interface = new rocket_c_file_interface( assets_root ); Rocket::Core::SetFileInterface( s_file_interface ); Rocket::Core::SetRenderInterface( s_render_interface ); Rocket::Core::SetSystemInterface( s_system_interface ); return Rocket::Core::Initialise(); } void nv::rocket_shutdown() { Rocket::Core::Shutdown(); delete s_render_interface; delete s_system_interface; delete s_file_interface; }