// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef toolx_sg_GL_manager #define toolx_sg_GL_manager #ifndef GL_VERSION_1_1 #error You have to include OpenGL header(s) before including this file. #endif #include #include #include #include #ifdef TOOLS_USE_GL_VERSION_3_2 #include #endif #include #include //::sqrt namespace toolx { namespace sg { template class gsto_elem: protected GL_FUNCTIONS { TOOLS_T_SCLASS(GL_FUNCTIONS,toolx::sg::gsto_elem) using parent_gl_functions = GL_FUNCTIONS; public: enum kind { kind_texture, kind_buffer, kind_list, kind_memory }; public: #ifdef TOOLS_USE_GL_VERSION_3_2 gsto_elem(kind a_kind,unsigned int a_gl_id,size_t a_size,const float* a_data,unsigned int a_vao_id = 0) :m_gl_id(a_gl_id) ,m_vao_id(a_vao_id) #else gsto_elem(kind a_kind,unsigned int a_gl_id,size_t a_size,const float* a_data) :m_gl_id(a_gl_id) #endif ,m_kind(a_kind) ,m_size(a_size) ,m_data(0) { if(a_data) { size_t num = m_size/sizeof(float); m_data = new float[num]; ::memcpy(m_data,a_data,m_size); } } virtual ~gsto_elem() { if(m_kind==kind_texture) { parent_gl_functions::gl__DeleteTextures(1,&m_gl_id); } else if(m_kind==kind_buffer) { #ifdef TOOLS_USE_GL_VERSION_3_2 parent_gl_functions::gl__DeleteBuffers(1,&m_gl_id); parent_gl_functions::gl__DeleteVertexArrays(1,&m_vao_id); #endif } else if(m_kind==kind_list) { if(m_gl_id) { #ifndef TOOLS_USE_GL_VERSION_3_2 parent_gl_functions::gl__DeleteLists(m_gl_id,1); #endif } } if(m_data) { delete [] m_data; } } private: gsto_elem(const gsto_elem& a_from) :parent_gl_functions(a_from) ,m_gl_id(a_from.m_gl_id) #ifdef TOOLS_USE_GL_VERSION_3_2 ,m_vao_id(a_from.m_vao_id) #endif ,m_kind(a_from.m_kind) ,m_size(a_from.m_size) ,m_data(0) { if(a_from.m_data) { size_t num = m_size/sizeof(float); m_data = new float[num]; ::memcpy(m_data,a_from.m_data,m_size); } } gsto_elem& operator=(const gsto_elem& a_from){ if(&a_from==this) return *this; m_gl_id = a_from.m_gl_id; #ifdef TOOLS_USE_GL_VERSION_3_2 m_vao_id = a_from.m_vao_id; #endif m_kind = a_from.m_kind; m_size = a_from.m_size; if(m_data) { delete [] m_data; m_data = 0; } if(a_from.m_data) { size_t num = m_size/sizeof(float); m_data = new float[num]; ::memcpy(m_data,a_from.m_data,m_size); } return *this; } public: bool is_valid() /*const*/ { if(!parent_gl_functions::initialize()) return false; if(m_kind==kind_texture) { return (parent_gl_functions::gl__IsTexture(m_gl_id)==GL_TRUE?true:false); } else if(m_kind==kind_buffer) { #ifdef TOOLS_USE_GL_VERSION_3_2 return (parent_gl_functions::gl__IsBuffer(m_gl_id)==GL_TRUE?true:false); //If ok, then VAO is ok too. #endif } else if(m_kind==kind_list) { #ifndef TOOLS_USE_GL_VERSION_3_2 return (parent_gl_functions::gl__IsList(m_gl_id)==GL_TRUE?true:false); #endif } else if(m_kind==kind_memory) { return true; } return false; } void bind() /*const*/ { parent_gl_functions::initialize(); if(m_kind==kind_texture) { parent_gl_functions::gl__BindTexture(GL_TEXTURE_2D,m_gl_id); } else if(m_kind==kind_buffer) { #ifdef TOOLS_USE_GL_VERSION_3_2 parent_gl_functions::gl__BindVertexArray(m_vao_id); parent_gl_functions::gl__BindBuffer(GL_ARRAY_BUFFER,m_gl_id); #endif } } public: unsigned int m_gl_id; #ifdef TOOLS_USE_GL_VERSION_3_2 unsigned int m_vao_id; #endif kind m_kind; size_t m_size; float* m_data; }; template class GL_manager_T : public virtual tools::sg::render_manager, protected GL_FUNCTIONS { typedef tools::sg::render_manager parent; using parent_gl_functions = GL_FUNCTIONS; typedef gsto_elem gsto_t; typedef typename std::map::const_iterator gstos_it_t; public: TOOLS_T_SCLASS(GL_FUNCTIONS,toolx::sg::GL_manager_T) virtual void* cast(const std::string& a_class) const { if(void* p = tools::cmp_cast(this,a_class)) {return p;} else return 0; } public: virtual bool begin_render(int a_x,int a_y,unsigned int a_ww,unsigned int a_wh, float a_r,float a_g,float a_b,float a_a,bool a_clear = true) { if (!parent_gl_functions::initialize()) { m_out << "toolx::sg::GL_manager_T::begin_render: parent_gl_functions::initialize() failed." << std::endl; return false; } if(m_gl_version.empty()) { {m_gl_version.clear(); const char* _sv = (const char*)parent_gl_functions::gl__GetString(GL_VERSION); if(_sv) m_gl_version = _sv;} #ifdef TOOLS_USE_GL_VERSION_3_2 {m_gl_shading_language_version.clear(); const char* _sv = (const char*)parent_gl_functions::gl__GetString(GL_SHADING_LANGUAGE_VERSION); if(_sv) m_gl_shading_language_version = _sv;} #endif {m_gl_vendor.clear(); const char* _sv = (const char*)parent_gl_functions::gl__GetString(GL_VENDOR); if(_sv) m_gl_vendor = _sv;} {m_gl_renderer.clear(); const char* _sv = (const char*)parent_gl_functions::gl__GetString(GL_RENDERER); if(_sv) m_gl_renderer = _sv;} {m_gl_extensions.clear(); const char* _sv = (const char*)parent_gl_functions::gl__GetString(GL_EXTENSIONS); if(_sv) m_gl_extensions = _sv;} m_gl_max_texture_size = 0; parent_gl_functions::gl__GetIntegerv(GL_MAX_TEXTURE_SIZE, &m_gl_max_texture_size); } clear_gl_errors(); #ifdef TOOLS_USE_GL_VERSION_3_2 // to avoid a 0x506 error message : if(parent_gl_functions::gl__CheckFramebufferStatus(GL_FRAMEBUFFER)!=GL_FRAMEBUFFER_COMPLETE) { return false; } if(!create_shader_program()) { dump_if_gl_errors(m_out,"toolx::sg::GL_manager_T::create_shader_program :"); m_out << "toolx::sg::GL_manager_T::begin_render : create_shader_program() failed." << std::endl; return false; } use_shader_program(); // WARNING : the values set here must match the default values in sg::state. //WARNING : the below glEnable/glDisable corresponds // to defaults in tools::sg::state. parent_gl_functions::gl__Enable(GL_DEPTH_TEST); parent_gl_functions::gl__FrontFace(GL_CCW); parent_gl_functions::gl__Enable(GL_CULL_FACE); parent_gl_functions::gl__Disable(GL_POLYGON_OFFSET_FILL); parent_gl_functions::gl__DepthFunc(GL_LESS); parent_gl_functions::gl__DepthMask(GL_TRUE); parent_gl_functions::gl__Disable(GL_BLEND); //must be in sync with tools::sg::state.m_GL_BLEND and sg::blend node default. parent_gl_functions::gl__BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); parent_gl_functions::gl__LineWidth(1.0f); parent_gl_functions::gl__Uniform1f(g_point_size_location(),1.0f); parent_gl_functions::gl__Viewport(a_x,a_y,a_ww,a_wh); if(a_clear) { parent_gl_functions::gl__ClearColor(a_r,a_g,a_b,a_a); parent_gl_functions::gl__Clear(GL_COLOR_BUFFER_BIT); parent_gl_functions::gl__Clear(GL_DEPTH_BUFFER_BIT); } tools::mat4f ID4;ID4.set_identity(); parent_gl_functions::gl__UniformMatrix4fv(g_model_proj_matrix_location(),1,GL_FALSE,ID4.data()); parent_gl_functions::gl__UniformMatrix4fv(g_normal_matrix_location(),1,GL_FALSE,ID4.data()); #else //not TOOLS_USE_GL_VERSION_3_2 #ifdef __APPLE__ #ifdef __COREFOUNDATION__ //Cocoa.h included: we build a Cocoa application. // to avoid a 0x506 error message : if(parent_gl_functions::gl__CheckFramebufferStatus(GL_FRAMEBUFFER)!=GL_FRAMEBUFFER_COMPLETE) { //It is a GL_VERSION_3_0 function. return false; } #endif #endif // WARNING : the values set here must match the default values in sg::state. parent_gl_functions::gl__Disable(GL_POLYGON_STIPPLE); //CoinGL : reading a .wrl having Material::transparency may enable GL_POLYGON_STIPPLE. parent_gl_functions::gl__Enable(GL_NORMALIZE); parent_gl_functions::gl__ShadeModel(GL_FLAT); parent_gl_functions::gl__Enable(GL_COLOR_MATERIAL); parent_gl_functions::gl__Disable(GL_BLEND); //must be in sync with tools::sg::state.m_GL_BLEND and sg::blend node default. parent_gl_functions::gl__BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); //WARNING : the below glEnable/glDisable corresponds // to defaults in tools::sg::state. parent_gl_functions::gl__Enable(GL_DEPTH_TEST); parent_gl_functions::gl__Disable(GL_LIGHTING); parent_gl_functions::gl__FrontFace(GL_CCW); parent_gl_functions::gl__Enable(GL_CULL_FACE); parent_gl_functions::gl__Disable(GL_POLYGON_OFFSET_FILL); parent_gl_functions::gl__Disable(GL_TEXTURE_2D); parent_gl_functions::gl__DepthFunc(GL_LESS); parent_gl_functions::gl__DepthMask(GL_TRUE); parent_gl_functions::gl__Disable(GL_POINT_SMOOTH); parent_gl_functions::gl__PointSize(1); parent_gl_functions::gl__Disable(GL_LINE_SMOOTH); //NOTE : it does not work on all platforms ! parent_gl_functions::gl__LineWidth(1); parent_gl_functions::gl__Viewport(a_x,a_y,a_ww,a_wh); if(a_clear) { parent_gl_functions::gl__ClearColor(a_r,a_g,a_b,0); parent_gl_functions::gl__Clear(GL_COLOR_BUFFER_BIT); parent_gl_functions::gl__Clear(GL_DEPTH_BUFFER_BIT); } parent_gl_functions::gl__MatrixMode(GL_PROJECTION); parent_gl_functions::gl__LoadIdentity(); parent_gl_functions::gl__MatrixMode(GL_MODELVIEW); parent_gl_functions::gl__LoadIdentity(); {parent_gl_functions::gl__Color4f(a_r,a_g,a_b,::sqrt(a_a)); float xyzs[12]; xyzs[0] = -1;xyzs[ 1] = -1;xyzs[ 2] = 0; xyzs[3] = 1;xyzs[ 4] = -1;xyzs[ 5] = 0; xyzs[6] = 1;xyzs[ 7] = 1;xyzs[ 8] = 0; xyzs[9] = -1;xyzs[10] = 1;xyzs[11] = 0; parent_gl_functions::gl__Disable(GL_DEPTH_TEST); parent_gl_functions::gl__EnableClientState(GL_VERTEX_ARRAY); parent_gl_functions::gl__VertexPointer(3,GL_FLOAT,0,xyzs); parent_gl_functions::gl__DrawArrays(GL_TRIANGLE_FAN,0,4); parent_gl_functions::gl__DisableClientState(GL_VERTEX_ARRAY); parent_gl_functions::gl__Enable(GL_DEPTH_TEST);} #endif //not TOOLS_USE_GL_VERSION_3_2 return true; } virtual void end_render() { #ifdef TOOLS_USE_GL_VERSION_3_2 reset_shader_locations(); #endif dump_if_gl_errors(m_out,"toolx::sg::GL_manager_T::end_render :"); } //////////////////////////////////////////////// /// texture : ////////////////////////////////// //////////////////////////////////////////////// virtual unsigned int create_texture(const tools::img_byte& a_img,bool a_NEAREST) { tools::img_byte* _img = 0; bool _img_delete = false; if(a_img.bytes_per_pixel()==1) { //exa eiffel-tower.png _img = new tools::img_byte; _img_delete = true; if(!a_img.bw2x(3,*_img)) { m_out << "toolx::sg::GL_manager_T::create_texture :" << " a_imgr.bw2x() failed." << std::endl; delete _img; return 0; } } else if(a_img.bytes_per_pixel()==3) { _img = const_cast(&a_img); } else if(a_img.bytes_per_pixel()==4) { //exa windusrf.png, text_freetype/font_pixmap. _img = const_cast(&a_img); } else { //should not happen. m_out << "toolx::sg::GL_manager_T::create_texture :" << " image with bytes_per_pixel " << a_img.bytes_per_pixel() << " not (yet) supported." << std::endl; return 0; } if(_img->is_empty()) { m_out << "toolx::sg::GL_manager_T::create_texture : img_byte is empty." << std::endl; if(_img_delete) delete _img; return 0; } {int sz; parent_gl_functions::gl__GetIntegerv(GL_MAX_TEXTURE_SIZE,&sz); if(!sz) { m_out << "toolx::sg::GL_manager_T::create_texture : warning : GL_MAX_TEXTURE_SIZE is zero." << std::endl; } else { tools::img_byte* res = new tools::img_byte; if(_img->check_gl_limit(sz,*res)) { if(res->is_empty()) { //_img does not exceed. delete res; } else { m_out << "toolx::sg::GL_manager_T::create_texture:" << " warning : img size (" << _img->size() << ") > GL_MAX_TEXTURE_SIZE (" << sz << "), we use a reduced one." << std::endl; if(_img_delete) delete _img; _img = res; _img_delete = true; } } else { m_out << "toolx::sg::GL_manager_T::create_texture :" << " warning : img size (" << _img->size() << ") > GL_MAX_TEXTURE_SIZE (" << sz << ") but can't reduce it." << std::endl; delete res; if(_img_delete) delete _img; return false; } }} if(_img->is_empty()) { m_out << "toolx::sg::GL_manager_T::create_texture : img_byte is empty." << std::endl; if(_img_delete) delete _img; return 0; } unsigned int gl_id; parent_gl_functions::gl__GenTextures(1,&gl_id); if(!gl_id) { m_out << "toolx::sg::GL_manager_T::create_texture : glGenTextures failed ()." << std::endl; if(_img_delete) delete _img; return 0; } unsigned int gsto_size = _img->size(); if(_img->bytes_per_pixel()==3) { parent_gl_functions::gl__BindTexture(GL_TEXTURE_2D,gl_id); parent_gl_functions::gl__TexImage2D(GL_TEXTURE_2D,0,GL_RGB,_img->width(),_img->height(),0,GL_RGB,GL_UNSIGNED_BYTE,_img->buffer()); if(_img_delete) delete _img; } else if(_img->bytes_per_pixel()==4) { parent_gl_functions::gl__BindTexture(GL_TEXTURE_2D,gl_id); parent_gl_functions::gl__TexImage2D(GL_TEXTURE_2D,0,GL_RGBA,_img->width(),_img->height(),0,GL_RGBA,GL_UNSIGNED_BYTE,_img->buffer()); if(_img_delete) delete _img; } else { m_out << "toolx::sg::GL_manager_T::create_texture : img.bytes_per_pixel() " << _img->bytes_per_pixel() << " not handled." << std::endl; if(_img_delete) delete _img; parent_gl_functions::gl__DeleteTextures(1,&gl_id); gl_id = 0; return false; } if(a_NEAREST) { parent_gl_functions::gl__TexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); //good to see astro images pixels. parent_gl_functions::gl__TexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); //idem. } else { parent_gl_functions::gl__TexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); parent_gl_functions::gl__TexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); } parent_gl_functions::gl__BindTexture(GL_TEXTURE_2D,0); if(dump_if_gl_errors(m_out,"toolx::sg::GL_manager_T::create_texture :")) { parent_gl_functions::gl__DeleteTextures(1,&gl_id); gl_id = 0; clear_gl_errors(); return 0; } unsigned int _id = m_gen_id;m_gen_id++; m_gstos[_id] = new gsto_t(gsto_t::kind_texture,gl_id,gsto_size,0); return _id; } //////////////////////////////////////////////// /// gsto /////////////////////////////////////// //////////////////////////////////////////////// virtual unsigned int create_gsto_from_data(size_t a_floatn,const float* a_data) { if(!a_floatn) return 0; switch(m_gsto_mode) { case tools::sg::gsto_gl_vbo:{ #ifdef TOOLS_USE_GL_VERSION_3_2 GLuint vao_id = 0; parent_gl_functions::gl__GenVertexArrays(1,&vao_id); if(!vao_id) { m_out << "toolx::sg::GL_manager_T::create_gsto_from_data : glGenVertexArray failed ()." << std::endl; return 0; } parent_gl_functions::gl__BindVertexArray(vao_id); //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// GLuint gl_id = 0; parent_gl_functions::gl__GenBuffers(1,&gl_id); if(!gl_id) { parent_gl_functions::gl__DeleteVertexArrays(1,&vao_id); m_out << "toolx::sg::GL_manager_T::create_gsto_from_data : glGenBuffers failed ()." << std::endl; return 0; } parent_gl_functions::gl__BindBuffer(GL_ARRAY_BUFFER,gl_id); parent_gl_functions::gl__BufferData(GL_ARRAY_BUFFER,a_floatn*sizeof(float),a_data,GL_STATIC_DRAW); parent_gl_functions::gl__BindBuffer(GL_ARRAY_BUFFER,0); //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// parent_gl_functions::gl__BindVertexArray(0); if(dump_if_gl_errors(m_out,"toolx::sg::GL_manager_T::create_gsto_from_data :")) { m_out << "toolx::sg::GL_manager_T::create_gsto_from_data :" << " a_floatn = " << a_floatn << ", a_data = " << a_data << ", vao_id " << vao_id << ", gl_id " << gl_id << std::endl; parent_gl_functions::gl__DeleteBuffers(1,&gl_id); parent_gl_functions::gl__DeleteVertexArrays(1,&vao_id); clear_gl_errors(); return 0; } unsigned int _id = m_gen_id;m_gen_id++; m_gstos[_id] = new gsto_t(gsto_t::kind_buffer,gl_id,a_floatn*sizeof(float),0,vao_id); return _id; #else m_out << "toolx::sg::GL_manager_T::create_gsto_from_data :" << " gsto mode is gl_vbo but class not compiled with TOOLS_USE_GL_VERSION_3_2." << std::endl; return 0; #endif }break; case tools::sg::gsto_gl_list:{ unsigned int gl_id = 0; unsigned int _id = m_gen_id;m_gen_id++; m_gstos[_id] = new gsto_t(gsto_t::kind_list,gl_id,a_floatn*sizeof(float),a_data); return _id; }break; case tools::sg::gsto_memory:{ unsigned int gl_id = 0; unsigned int _id = m_gen_id;m_gen_id++; m_gstos[_id] = new gsto_t(gsto_t::kind_memory,gl_id,a_floatn*sizeof(float),a_data); return _id; }break; } return 0; } virtual bool is_gsto_id_valid(unsigned int a_id) const { gstos_it_t it = m_gstos.find(a_id); if(it==m_gstos.end()) return false; return (*it).second->is_valid(); } virtual void delete_gsto(unsigned int a_id){ tools::delete_key(m_gstos,a_id); } //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// float* gsto_data(unsigned int a_id) const { gstos_it_t it = m_gstos.find(a_id); if(it==m_gstos.end()) return 0; return (*it).second->m_data; } unsigned int gsto_gl_list_id(unsigned int a_id,bool& a_created) { gstos_it_t it = m_gstos.find(a_id); if(it==m_gstos.end()) {a_created = false;return 0;} if((*it).second->m_kind!=gsto_t::kind_list) {a_created = false;return 0;} if((*it).second->m_gl_id) { a_created = false; return (*it).second->m_gl_id; } else { #ifndef TOOLS_USE_GL_VERSION_3_2 unsigned int _id = parent_gl_functions::gl__GenLists(1); if(!_id) {a_created = false;return 0;} a_created = true; (*it).second->m_gl_id = _id; return _id; #else a_created = false; return 0; #endif } } //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// virtual tools::sg::gsto_mode get_gsto_mode() const {return m_gsto_mode;} virtual void set_gsto_mode(tools::sg::gsto_mode a_v) { if(a_v==m_gsto_mode) return; tools::safe_clear(m_gstos); switch(a_v) { case tools::sg::gsto_gl_vbo:{ #ifdef TOOLS_USE_GL_VERSION_3_2 m_gsto_mode = a_v; #else m_gsto_mode = tools::sg::gsto_memory; #endif }break; case tools::sg::gsto_gl_list:{ #ifndef TOOLS_USE_GL_VERSION_3_2 m_gsto_mode = a_v; #else m_gsto_mode = tools::sg::gsto_memory; #endif }break; case tools::sg::gsto_memory:{ m_gsto_mode = tools::sg::gsto_memory; }break; } } virtual void available_gsto_modes(std::vector& a_vs) { a_vs.clear(); #ifdef TOOLS_USE_GL_VERSION_3_2 a_vs.push_back(tools::sg::s_gsto_gl_vbo()); #endif #ifndef TOOLS_USE_GL_VERSION_3_2 a_vs.push_back(tools::sg::s_gsto_gl_list()); #endif a_vs.push_back(tools::sg::s_gsto_memory()); } virtual void available_not_memory_gsto_mode(std::string& a_v) const { a_v.clear(); #ifdef TOOLS_USE_GL_VERSION_3_2 a_v = tools::sg::s_gsto_gl_vbo(); #else a_v = tools::sg::s_gsto_gl_list(); #endif } virtual size_t used_texture_memory() const { size_t sz = 0; gstos_it_t it; for(it=m_gstos.begin();it!=m_gstos.end();++it) { if((*it).second->m_kind==gsto_t::kind_texture) sz += (*it).second->m_size; } return sz; } virtual size_t gstos_size() const { size_t sz = 0; gstos_it_t it; for(it=m_gstos.begin();it!=m_gstos.end();++it) sz += (*it).second->m_size; return sz; } public: GL_manager_T(std::ostream& a_out) :m_out(a_out) ,m_gen_id(1) ,m_gsto_mode(tools::sg::gsto_memory) #ifdef TOOLS_USE_GL_VERSION_3_2 ,m_fragment_shader(0) ,m_vertex_shader(0) ,m_shader_program(0) #endif ,m_gl_max_texture_size(0) {} virtual ~GL_manager_T(){ #ifdef TOOLS_USE_GL_VERSION_3_2 delete_shader_program(); #endif tools::safe_clear(m_gstos); } public: GL_manager_T(const GL_manager_T& a_from) :parent(a_from) ,parent_gl_functions(a_from) ,m_out(a_from.m_out) ,m_gen_id(a_from.m_gen_id) ,m_gsto_mode(a_from.m_gsto_mode) ,m_gl_max_texture_size(0) {} GL_manager_T& operator=(const GL_manager_T& a_from){ if(&a_from==this) return *this; m_gen_id = a_from.m_gen_id; m_gsto_mode = a_from.m_gsto_mode; tools::safe_clear(m_gstos); return *this; } public: void bind_gsto(unsigned int a_id) { gstos_it_t it = m_gstos.find(a_id); if(it==m_gstos.end()) return; (*it).second->bind(); } void unbind_gsto() { #ifdef TOOLS_USE_GL_VERSION_3_2 parent_gl_functions::gl__BindBuffer(GL_ARRAY_BUFFER,0); parent_gl_functions::gl__BindVertexArray(0); #endif } void delete_gstos() { tools::safe_clear(m_gstos); } public: unsigned char* get_rgbas(unsigned int a_w,unsigned int a_h) { unsigned char* rgbas = new unsigned char[4 * a_w * a_h]; if(!rgbas) return 0; parent_gl_functions::gl__PixelStorei(GL_PACK_ALIGNMENT,1); //needed with Cocoa. parent_gl_functions::gl__ReadPixels(0,0,a_w,a_h,GL_RGBA,GL_UNSIGNED_BYTE,rgbas); return rgbas; } unsigned char* get_rgbs(unsigned int a_w,unsigned int a_h) { unsigned char* rgbs = new unsigned char[3 * a_w * a_h]; if(!rgbs) return 0; parent_gl_functions::gl__PixelStorei(GL_PACK_ALIGNMENT,1); //needed with Cocoa. parent_gl_functions::gl__ReadPixels(0,0,a_w,a_h,GL_RGB,GL_UNSIGNED_BYTE,rgbs); return rgbs; } #ifdef TOOLS_USE_GL_VERSION_3_2 bool create_shader_program() { if(m_shader_program) return true; //done unsigned int gl_glsl_major_version = 0; unsigned int gl_glsl_minor_version = 0; if(!get_version_numbers(m_out,m_gl_shading_language_version,gl_glsl_major_version,gl_glsl_minor_version)) { m_out << "toolx::sg::GL_manager_T::create_shader_program :" << " can't get version numnbers for GL_SHADING_LANGUAGE_VERSION." << std::endl; return false; } unsigned int glsl_shader_processor_version = gl_glsl_major_version*100+gl_glsl_minor_version; if(glsl_shader_processor_version<150) { m_out << "toolx::sg::GL_manager_T::create_shader_program :" << " glsl_shader_processor_version is " << glsl_shader_processor_version << ", but only the ones greater or equal to 150 are supported." << std::endl; return false; } {unsigned int gl_major_version = 0; unsigned int gl_minor_version = 0; if(!get_version_numbers(m_out,m_gl_version,gl_major_version,gl_minor_version)) { m_out << "toolx::sg::GL_manager_T::create_shader_program :" << " can't get version numbers for GL_VERSION." << std::endl; gl_major_version = 0; gl_minor_version = 0; } m_out << "toolx::sg::GL_manager_T::create_shader_program: infos:" << std::endl << " GL_VERSION " << gl_major_version << "." << gl_minor_version << std::endl << " GL_SHADING_LANGUAGE_VERSION " << gl_glsl_major_version << "." << gl_glsl_minor_version << std::endl << " shader processor version " << glsl_shader_processor_version << std::endl;} //////////////////////////////////////////////////// //////////////////////////////////////////////////// static const GLchar* str_fs_150 = "\ #version 150\n\ uniform sampler2D sampler;\n\ in vec4 frag_color;\n\ in float frag_tex_on;\n\ \n\ in vec2 frag_tex_coords;\n\ out vec4 _gl_FragColor;\n\ void main(void) {\n\ if(frag_tex_on==1.0) {\n\ _gl_FragColor = frag_color*texture(sampler,vec2(frag_tex_coords.s,frag_tex_coords.t));\n\ } else {\n\ _gl_FragColor = frag_color;\n\ }\n\ }\n\ "; m_fragment_shader = parent_gl_functions::gl__CreateShader(GL_FRAGMENT_SHADER); if(!m_fragment_shader) { m_out << "toolx::sg::GL_manager_T::create_shader_program :" << " glCreateShader(GL_FRAGMENT_SHADER) failed." << std::endl; return false; } char* str_fs = (char*)str_fs_150; parent_gl_functions::gl__ShaderSource(m_fragment_shader,1,&str_fs,NULL); parent_gl_functions::gl__CompileShader(m_fragment_shader); {GLint status; parent_gl_functions::gl__GetShaderiv(m_fragment_shader,GL_COMPILE_STATUS,&status); if(status==GL_FALSE) { m_out << "toolx::sg::GL_manager_T::create_shader_program :" << " compilation of the fragment shader failed." << std::endl; {GLsizei log_size; char shader_log[4096]; parent_gl_functions::gl__GetShaderInfoLog(m_fragment_shader,1024,&log_size,shader_log); m_out << "toolx::sg::GL_manager_T::create_shader_program: fragment shader_log size " << log_size << std::endl; m_out << std::string(shader_log) << std::endl;} parent_gl_functions::gl__DeleteShader(m_fragment_shader); m_fragment_shader = 0; return false; }} //////////////////////////////////////////////////// //////////////////////////////////////////////////// static const GLchar* str_vs_150 = "\ #version 150\n\ in vec3 one_pos;\n\ in vec3 one_normal;\n\ in vec4 one_color;\n\ in vec2 one_tex;\n\ uniform mat4 model_proj_matrix;\n\ uniform mat4 normal_matrix;\n\ uniform vec4 color;\n\ uniform bool pos_color;\n\ uniform vec3 normal;\n\ uniform bool pos_normal;\n\ uniform bool tex_on;\n\ uniform bool light_on;\n\ uniform vec3 light_direction;\n\ uniform vec4 light_color;\n\ uniform vec4 light_ambient;\n\ uniform float point_size;\n\ \n\ out float frag_tex_on;\n\ out vec4 frag_color;\n\ out vec2 frag_tex_coords;\n\ \n\ void main(void) {\n\ frag_tex_on = 0.0;\n\ if(tex_on) frag_tex_on = 1.0;\n\ frag_tex_coords = one_tex;\n\ if(tex_on) {\n\ frag_color = color;\n\ } else {\n\ frag_color = color;\n\ if(pos_color) frag_color = one_color;\n\ }\n\ /*frag_color = clamp(frag_color,0,1);*/\n\ \n\ if(light_on) {\n\ vec3 _normal = normal;\n\ if(pos_normal) _normal = one_normal;\n\ vec3 frag_normal = vec3(normalize(normal_matrix*vec4(_normal,0)));\n\ /* at this point, light_direction should be normalized.*/\n\ vec4 ambient_color = light_ambient;\n\ /*ambient_color = clamp(ambient_color,0,1);*/\n\ float cooking = 1.4; /*to have same intensity as GL-1.1.*/\n\ float _dot = dot(frag_normal,light_direction);\n\ if(_dot<0.0) {\n\ _dot *= -1.0;\n\ vec4 diffuse_color = light_color*_dot*cooking;\n\ /*diffuse_color = clamp(diffuse_color,0,1);*/\n\ vec4 _color = (ambient_color+diffuse_color)*frag_color;\n\ frag_color = vec4(_color.rgb,frag_color.a);\n\ } else {\n\ frag_color = ambient_color*frag_color*cooking;\n\ }\n\ }\n\ gl_PointSize = point_size;\n\ gl_Position = model_proj_matrix * vec4(one_pos,1);\n\ }\n\ "; m_vertex_shader = parent_gl_functions::gl__CreateShader(GL_VERTEX_SHADER); if(!m_vertex_shader) { m_out << "toolx::sg::GL_manager_T::create_shader_program :" << " glCreateShader(GL_VERTEX_SHADER) failed." << std::endl; parent_gl_functions::gl__DeleteShader(m_fragment_shader); m_fragment_shader = 0; return false; } char* str_vs = (char*)str_vs_150; parent_gl_functions::gl__ShaderSource(m_vertex_shader,1,&str_vs,NULL); parent_gl_functions::gl__CompileShader(m_vertex_shader); {GLint status; parent_gl_functions::gl__GetShaderiv(m_vertex_shader,GL_COMPILE_STATUS,&status); if(status==GL_FALSE) { m_out << "toolx::sg::GL_manager_T::create_shader_program :" << " compilation of the vertex shader failed." << std::endl; {GLsizei log_size; char shader_log[4096]; parent_gl_functions::gl__GetShaderInfoLog(m_fragment_shader,1024,&log_size,shader_log); m_out << "toolx::sg::GL_manager_T::create_shader_program: vertex shader_log size " << log_size << std::endl; m_out << std::string(shader_log) << std::endl;} parent_gl_functions::gl__DeleteShader(m_vertex_shader); parent_gl_functions::gl__DeleteShader(m_fragment_shader); m_vertex_shader = 0; m_fragment_shader = 0; return false; }} m_shader_program = parent_gl_functions::gl__CreateProgram(); parent_gl_functions::gl__AttachShader(m_shader_program,m_fragment_shader); parent_gl_functions::gl__AttachShader(m_shader_program,m_vertex_shader); parent_gl_functions::gl__LinkProgram(m_shader_program); {GLint status; parent_gl_functions::gl__GetProgramiv(m_shader_program,GL_LINK_STATUS,&status); if(status==GL_FALSE) { m_out << "toolx::sg::GL_manager_T::create_shader_program :" << " link program failed." << std::endl; parent_gl_functions::gl__DetachShader(m_shader_program,m_fragment_shader); parent_gl_functions::gl__DetachShader(m_shader_program,m_vertex_shader); parent_gl_functions::gl__DeleteProgram(m_shader_program); parent_gl_functions::gl__DeleteShader(m_vertex_shader); parent_gl_functions::gl__DeleteShader(m_fragment_shader); m_vertex_shader = 0; m_fragment_shader = 0; m_shader_program = 0; return false; }} return true; } void delete_shader_program() { if(!m_shader_program) return; parent_gl_functions::gl__DetachShader(m_shader_program,m_fragment_shader); parent_gl_functions::gl__DetachShader(m_shader_program,m_vertex_shader); parent_gl_functions::gl__DeleteProgram(m_shader_program); parent_gl_functions::gl__DeleteShader(m_vertex_shader); parent_gl_functions::gl__DeleteShader(m_fragment_shader); m_vertex_shader = 0; m_fragment_shader = 0; m_shader_program = 0; } #define GET_UNIFORM_LOCATION(a__var,a__name)\ m_##a__var = parent_gl_functions::gl__GetUniformLocation(m_shader_program,a__name);\ if(m_##a__var==(-1)) {\ m_out << "toolx::sg::GL_manager_T::use_shader_program : glGetUniformLocation(" << a__name << ") failed." << std::endl;\ } #define GET_ATTRIB_LOCATION(a__var,a__name)\ m_##a__var = parent_gl_functions::gl__GetAttribLocation(m_shader_program,a__name);\ if(m_##a__var==(-1)) {\ m_out << "toolx::sg::GL_manager_T::use_shader_program : glGetAttribLocation(" << a__name << ") failed." << std::endl;\ } GLint g_sampler_location() {return m_g_sampler;} GLint g_tex_on_location() {return m_g_tex_on;} GLint g_point_size_location() {return m_g_point_size;} GLint g_one_pos_location() {return m_g_one_pos;} GLint g_one_normal_location() {return m_g_one_normal;} GLint g_one_color_location() {return m_g_one_color;} GLint g_one_tex_location() {return m_g_one_tex;} GLint g_model_proj_matrix_location() {return m_g_model_proj_matrix;} GLint g_normal_matrix_location() {return m_g_normal_matrix;} GLint g_color_location() {return m_g_color;} GLint g_pos_color_location() {return m_g_pos_color;} GLint g_normal_location() {return m_g_normal;} GLint g_pos_normal_location() {return m_g_pos_normal;} GLint g_light_on_location() {return m_g_light_on;} GLint g_light_direction_location() {return m_g_light_direction;} GLint g_light_color_location() {return m_g_light_color;} GLint g_light_ambient_location() {return m_g_light_ambient;} void use_shader_program() { if(!m_shader_program) return; parent_gl_functions::gl__UseProgram(m_shader_program); // used by renderer : GET_UNIFORM_LOCATION(g_sampler,"sampler") GET_UNIFORM_LOCATION(g_tex_on,"tex_on") GET_UNIFORM_LOCATION(g_point_size,"point_size") GET_ATTRIB_LOCATION(g_one_pos,"one_pos") GET_ATTRIB_LOCATION(g_one_normal,"one_normal") GET_ATTRIB_LOCATION(g_one_color,"one_color") GET_ATTRIB_LOCATION(g_one_tex,"one_tex") GET_UNIFORM_LOCATION(g_model_proj_matrix,"model_proj_matrix") GET_UNIFORM_LOCATION(g_normal_matrix,"normal_matrix") GET_UNIFORM_LOCATION(g_color,"color") GET_UNIFORM_LOCATION(g_pos_color,"pos_color") GET_UNIFORM_LOCATION(g_normal,"normal") GET_UNIFORM_LOCATION(g_pos_normal,"pos_normal") GET_UNIFORM_LOCATION(g_light_on,"light_on") GET_UNIFORM_LOCATION(g_light_direction,"light_direction") GET_UNIFORM_LOCATION(g_light_color,"light_color") GET_UNIFORM_LOCATION(g_light_ambient,"light_ambient") parent_gl_functions::gl__Uniform1i(m_g_tex_on,0); parent_gl_functions::gl__Uniform1f(m_g_point_size,1); parent_gl_functions::gl__Uniform4f(m_g_color,1,1,1,1); parent_gl_functions::gl__Uniform1i(m_g_pos_color,false); parent_gl_functions::gl__Uniform3f(m_g_normal,0,0,1); parent_gl_functions::gl__Uniform1i(m_g_pos_normal,false); parent_gl_functions::gl__Uniform1i(m_g_light_on,0); parent_gl_functions::gl__Uniform4f(m_g_light_color,1,1,1,0); parent_gl_functions::gl__Uniform3f(m_g_light_direction,0,0,-1); } void reset_shader_locations() { m_g_sampler = -1; m_g_tex_on = -1; m_g_point_size = -1; m_g_one_pos = -1; m_g_one_normal = -1; m_g_one_color = -1; m_g_one_tex = -1; m_g_model_proj_matrix = -1; m_g_normal_matrix = -1; m_g_color = -1; m_g_pos_color = -1; m_g_normal = -1; m_g_pos_normal = -1; m_g_light_on = -1; m_g_light_direction = -1; m_g_light_color = -1; m_g_light_ambient = -1; } #undef GET_ATTRIB_LOCATION #undef GET_UNIFORM_LOCATION #endif //TOOLS_USE_GL_VERSION_3_2 protected: /*static*/ void clear_gl_errors() { GLenum glerror = parent_gl_functions::gl__GetError(); while(glerror!=GL_NO_ERROR) { glerror = parent_gl_functions::gl__GetError(); } } /*static*/ bool dump_if_gl_errors(std::ostream& a_out,const std::string& a_head) { bool retval = false; GLenum glerror = parent_gl_functions::gl__GetError(); if(glerror!=GL_NO_ERROR) { a_out << a_head << " we have gl errors :" << std::endl; retval = true; } while(glerror!=GL_NO_ERROR) { //#define GL_NO_ERROR 0 //#define GL_INVALID_ENUM 0x0500 //#define GL_INVALID_VALUE 0x0501 //#define GL_INVALID_OPERATION 0x0502 //#define GL_STACK_OVERFLOW 0x0503 //#define GL_STACK_UNDERFLOW 0x0504 //#define GL_OUT_OF_MEMORY 0x0505 //#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 std::ostringstream oss; oss << "0x" << std::hex << glerror; a_out << oss.str() << std::endl; glerror = parent_gl_functions::gl__GetError(); } return retval; } static bool s2vers(const std::string& a_string,std::vector& a_versions) { // a_string is of the form "[..] , extract . // Examples : "1.0.2" "2.1 ATI-4.8.101" a_versions.clear(); if(a_string.empty()) return false; char* pos = (char*)a_string.c_str(); while((*pos!='\0')&&(*pos!=' ')) { char* s; long v = ::strtol(pos,&s,10); if(s==pos) { a_versions.clear(); return false; } if(v<0) { a_versions.clear(); return false; } a_versions.push_back((unsigned int)v); pos = s; //Could be at end. if(*pos=='\0') break; if(*pos==' ') break; if(*pos!='.') { a_versions.clear(); return false; } // Next char is '.', skip it: pos++; } return true; } /*static*/ bool get_version_numbers(std::ostream& a_out,const std::string& a_version,unsigned int& a_major,unsigned int& a_minor) { a_major = 0; a_minor = 0; std::vector verss; if(!s2vers(a_version,verss)) { a_out << "toolx::sg::GL_manager_T::get_version_numbers :" << " toolx::sg::GL_manager_T::s2vers() failed on \"" << a_version << "\"." << std::endl; return false; } if(verss.size()==1) { a_major = verss[0]; a_minor = 0; } else if(verss.size()>=2) { a_major = verss[0]; a_minor = verss[1]; } else { a_out << "toolx::sg::GL_manager_T::get_version_numbers :" << " can't extract major/minor version number from \"" << a_version << "\"." << std::endl; return false; } return true; } protected: std::ostream& m_out; std::map m_gstos; unsigned int m_gen_id; tools::sg::gsto_mode m_gsto_mode; #ifdef TOOLS_USE_GL_VERSION_3_2 GLuint m_fragment_shader; GLuint m_vertex_shader; GLuint m_shader_program; //locations: GLint m_g_sampler; GLint m_g_tex_on; GLint m_g_point_size; GLint m_g_one_pos; GLint m_g_one_normal; GLint m_g_one_color; GLint m_g_one_tex; GLint m_g_model_proj_matrix; GLint m_g_normal_matrix; GLint m_g_color; GLint m_g_pos_color; GLint m_g_normal; GLint m_g_pos_normal; GLint m_g_light_on; GLint m_g_light_direction; GLint m_g_light_color; GLint m_g_light_ambient; #endif //TOOLS_USE_GL_VERSION_3_2 std::string m_gl_version; #ifdef TOOLS_USE_GL_VERSION_3_2 std::string m_gl_shading_language_version; #endif std::string m_gl_vendor; std::string m_gl_renderer; std::string m_gl_extensions; GLint m_gl_max_texture_size; }; }} #endif