Files
geant4/source/analysis/g4tools/include/tools/sg/gl_manager
T
2020-06-26 10:23:25 +02:00

651 lines
19 KiB
Plaintext

// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.
#ifndef tools_sg_GL_manager
#define tools_sg_GL_manager
#include "gl"
#include "../glbuf"
#include <tools/sg/render_manager>
#include <tools/mapmanip>
#include <cmath> //::sqrt
#ifdef TOOLS_MEM
#include <tools/mem>
#endif
namespace tools {
namespace sg {
class GL_manager : public virtual tools::sg::render_manager {
typedef tools::sg::render_manager parent;
public:
TOOLS_SCLASS(tools::sg::GL_manager)
virtual void* cast(const std::string& a_class) const {
if(void* p = tools::cmp_cast<GL_manager>(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) {
gl_clear_errors();
#if TARGET_OS_IPHONE
#elif defined(ANDROID)
#elif _WIN32
#elif __APPLE__ // Cocoa
// to avoid a 0x506 error message :
#ifdef TOOLS_HAS_GL_VBO
#if GL_ARB_framebuffer_object
if(::glCheckFramebufferStatus(GL_FRAMEBUFFER)!=GL_FRAMEBUFFER_COMPLETE) {
//m_out << "tools::sg::GL_manager::begin_render :"
// << " frame buffer not complete."
// << std::endl;
return false;
}
#endif //GL_ARB_framebuffer_object
#endif //TOOLS_HAS_GL_VBO
#else
#endif
// WARNING : the values set here must match the default values in sg::state.
// Antialiasing :
#if TARGET_OS_IPHONE
#elif defined(ANDROID)
::glEnable(GL_MULTISAMPLE);
#elif _WIN32
#elif __APPLE__
::glEnable(GL_MULTISAMPLE); //Cocoa
#else
#endif
/* NOTE : not GL-ES :
// ::glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
// The upper is cruely lacking with povama solid+light.
// ::glEnable(GL_LINE_STIPPLE);
::glDisable(GL_POLYGON_SMOOTH);
::glAccum(GL_LOAD,1.0f);
::glAccum(GL_RETURN,1.0f);
::glReadBuffer(GL_FRONT);
NOTE : GL_POLYGON_SMOOTH is here on Cocoa/AGL, Windows/WGL but it
does nothing.
*/
#if TARGET_OS_IPHONE
// GL-ES
#elif defined(ANDROID)
// GL-ES
#else
::glDisable(GL_POLYGON_STIPPLE); //CoinGL : reading a .wrl having Material::transparency may enable GL_POLYGON_STIPPLE.
#endif
::glEnable(GL_NORMALIZE);
::glShadeModel(GL_FLAT);
//::glShadeModel(GL_SMOOTH); //Gouraud
// GL-ES : ::glMaterialfv does not work. We then use :
// ::glEnable(GL_COLOR_MATERIAL) and ::glColor.
::glEnable(GL_COLOR_MATERIAL);
// to handle transparency (same as SoGLRenderAction::SCREEN_DOOR ?) :
//::glEnable(GL_BLEND);
// NOTE : with Cocoa+AppleGL on macOS-10.14 (Mojave), it appears that points are blended ! (Even if alpha color is 1).
// Seen with some simulated catalog of galaxies done for LSST/DC2.
// To master this, we disable GL_BLEND by default. Then people wanting some transparency have to
// use the tools::sg::blend node in their scene graph.
::glDisable(GL_BLEND); //must be in sync with tools::sg::state.m_GL_BLEND and sg::blend node default.
::glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
//WARNING : the below glEnable/glDisable corresponds
// to defaults in tools::sg::state.
::glEnable(GL_DEPTH_TEST);
::glDisable(GL_LIGHTING);
::glFrontFace(GL_CCW);
::glEnable(GL_CULL_FACE);
::glDisable(GL_POLYGON_OFFSET_FILL);
::glDisable(GL_TEXTURE_2D);
::glDisable(GL_POINT_SMOOTH);
::glPointSize(1);
::glDisable(GL_LINE_SMOOTH); //NOTE : it does not work on all platforms !
::glLineWidth(1);
::glViewport(a_x,a_y,a_ww,a_wh);
// NOTE : iOS : glScissor logic does not work properly with Apple multisampling.
// But it appears that it is not needed for VR split views.
// Here a combination glViewport+[correct camera lrbt] does the clipping job (???).
// (But the clear color should be the same for both views).
//::glEnable(GL_SCISSOR_TEST);
//::glScissor(a_x,a_y,a_ww,a_wh);
//NOTE : a=0 coworks with the below logic to handle transparent background.
if(a_clear) {
::glClearColor(a_r,a_g,a_b,0);
::glClear(GL_COLOR_BUFFER_BIT);
::glClear(GL_DEPTH_BUFFER_BIT);
}
//WARNING : Android, iPhone : only one glPushMatrix ok !
::glMatrixMode(GL_PROJECTION);
::glLoadIdentity();
//WARNING : we take the convention that the current mode is MODELVIEW
::glMatrixMode(GL_MODELVIEW);
::glLoadIdentity();
//m_clear_color.set_a(0.2);
// to handle a transparent background :
//NOTE : not tested on Android and iOS.
// dst color :
// cr * alpha + cr * (1-alpha) = cr
// cg * alpha + cg * (1-alpha) = cg
// cb * alpha + cb * (1-alpha) = cb
// alpha*alpha + 0 * (1-alpha) = ca => alpha = sqrt(ca)
{::glColor4f(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;
::glDisable(GL_DEPTH_TEST);
::glEnableClientState(GL_VERTEX_ARRAY);
::glVertexPointer(3,GL_FLOAT,0,xyzs);
::glDrawArrays(GL_TRIANGLE_FAN,0,4);
::glDisableClientState(GL_VERTEX_ARRAY);
::glEnable(GL_DEPTH_TEST);}
return true;
}
virtual void end_render() {
::glFinish();
gl_dump_if_errors(m_out,"tools::sg::GL_manager::end_render :");
}
////////////////////////////////////////////////
/// texture : //////////////////////////////////
////////////////////////////////////////////////
virtual unsigned int create_texture(const tools::img_byte& a_img,bool a_NEAREST) {
unsigned int gl_id;
::glGenTextures(1,&gl_id);
if(!gl_id) return 0;
#ifdef TOOLS_MEM
tools::mem::increment(tools::s_tex().c_str());
#endif
unsigned int gsto_size = a_img.size();
::glBindTexture(GL_TEXTURE_2D,gl_id);
bool status = true;
{int sz;
::glGetIntegerv(GL_MAX_TEXTURE_SIZE,&sz);
// MacBookPro : it returns 8192.
// Android : it returns 2048.
// iPad1 : it returns 2048.
// iPod : it returns ?
//::printf("debug : GL_MAX_TEXTURE_SIZE : %d\n",sz);
tools::img_byte res;
if(!sz) {
m_out << "tools::sg::gl::tex_img : warning : GL_MAX_TEXTURE_SIZE is zero." << std::endl;
status = gl_tex_img(m_out,a_img);
} else {
if(a_img.check_gl_limit(sz,res)) {
if(res.is_empty()) { //a_img does not exceed.
status = gl_tex_img(m_out,a_img);
} else {
m_out << "tools::sg::gl::tex_img : warning : img size > GL_MAX_TEXTURE_SIZE (" << sz << ")." << std::endl;
status = gl_tex_img(m_out,res);
gsto_size = res.size();
}
} else {
m_out << "tools::sg::gl::tex_img :"
<< " warning : img size > GL_MAX_TEXTURE_SIZE (" << sz << ") but can't reduce."
<< std::endl;
status = false;
}}}
if(a_NEAREST) {
::glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); //good to see astro images pixels.
::glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); //idem.
} else {
::glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
}
::glBindTexture(GL_TEXTURE_2D,0);
if(!status) {
gl_dump_if_errors(m_out,"tools::sg::GL_manager::create_texture (1) :");
::glDeleteTextures(1,&gl_id);
#ifdef TOOLS_MEM
tools::mem::decrement(tools::s_tex().c_str());
#endif
gl_id = 0;
gl_clear_errors();
return 0;
}
if(gl_dump_if_errors(m_out,"tools::sg::GL_manager::create_texture (2) :")) {
::glDeleteTextures(1,&gl_id);
#ifdef TOOLS_MEM
tools::mem::decrement(tools::s_tex().c_str());
#endif
gl_id = 0;
gl_clear_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;
}
////////////////////////////////////////////////
/// VBO ////////////////////////////////////////
////////////////////////////////////////////////
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_HAS_GL_VBO
unsigned int gl_id = 0;
::glGenBuffers(1,&gl_id);
if(!gl_id) {
if(!m_warned) {
m_warned = true;
m_out << "tools::sg::GL_manager::create_gsto_from_data : glGenBuffers failed ()." << std::endl;
}
return 0;
}
#ifdef TOOLS_MEM
tools::mem::increment(tools::s_gsto().c_str());
#endif
::glBindBuffer(GL_ARRAY_BUFFER,gl_id);
::glBufferData(GL_ARRAY_BUFFER,
a_floatn*sizeof(float),a_data,
GL_STATIC_DRAW);
::glBindBuffer(GL_ARRAY_BUFFER,0);
if(gl_dump_if_errors(m_out,"tools::sg::GL_manager::create_gsto_from_data :")) {
::glDeleteBuffers(1,&gl_id);
#ifdef TOOLS_MEM
tools::mem::decrement(tools::s_gsto().c_str());
#endif
gl_id = 0;
gl_clear_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);
return _id;
#else //!TOOLS_HAS_GL_VBO
m_out << "tools::sg::GL_manager::create_gsto_from_data :"
<< " gsto mode is gl_vbo but class not compiled with TOOLS_HAS_GL_VBO."
<< std::endl;
return 0;
#endif //TOOLS_HAS_GL_VBO
}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 {
std::map<unsigned int,gsto_t*>::const_iterator 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<unsigned int,gsto_t>(m_gstos,a_id);
}
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
float* gsto_data(unsigned int a_id) const {
std::map<unsigned int,gsto_t*>::const_iterator 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) const {
std::map<unsigned int,gsto_t*>::const_iterator 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 {
#ifdef TOOLS_HAS_GL_LIST
unsigned int _id = ::glGenLists(1);
if(!_id) {a_created = false;return 0;}
#ifdef TOOLS_MEM
tools::mem::increment(tools::s_gsto().c_str());
#endif
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<unsigned int,gsto_t>(m_gstos);
switch(a_v) {
case tools::sg::gsto_gl_vbo:{
#ifdef TOOLS_HAS_GL_VBO
m_gsto_mode = a_v;
#else
m_gsto_mode = tools::sg::gsto_memory;
#endif
}break;
case tools::sg::gsto_gl_list:{
#ifdef TOOLS_HAS_GL_LIST
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<std::string>& a_vs) {
a_vs.clear();
#ifdef TOOLS_HAS_GL_VBO
a_vs.push_back(tools::sg::s_gsto_gl_vbo());
#endif
#ifdef TOOLS_HAS_GL_LIST
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_HAS_GL_VBO
a_v = tools::sg::s_gsto_gl_vbo();
#endif
#ifdef TOOLS_HAS_GL_LIST
if(a_v.empty()) a_v = tools::sg::s_gsto_gl_list();
#endif
}
virtual size_t used_texture_memory() const {
size_t sz = 0;
std::map<unsigned int,gsto_t*>::const_iterator 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;
std::map<unsigned int,gsto_t*>::const_iterator it;
for(it=m_gstos.begin();it!=m_gstos.end();++it) sz += (*it).second->m_size;
return sz;
}
public:
GL_manager(std::ostream& a_out)
:m_out(a_out)
,m_gen_id(1)
#ifdef TOOLS_HAS_GL_VBO
,m_gsto_mode(tools::sg::gsto_gl_vbo) //priority to GL VBOs.
#elif TOOLS_HAS_GL_LIST
,m_gsto_mode(tools::sg::gsto_gl_list)
#else
,m_gsto_mode(tools::sg::gsto_memory)
#endif
,m_warned(false)
{}
virtual ~GL_manager(){
tools::safe_clear<unsigned int,gsto_t>(m_gstos);
}
public:
GL_manager(const GL_manager& a_from)
:parent(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_warned(false)
{}
GL_manager& operator=(const GL_manager& 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<unsigned int,gsto_t>(m_gstos);
m_warned = false;
return *this;
}
public:
void bind_gsto(unsigned int a_id) {
std::map<unsigned int,gsto_t*>::const_iterator it = m_gstos.find(a_id);
if(it==m_gstos.end()) return;
(*it).second->bind();
}
void delete_gstos() {
tools::safe_clear<unsigned int,gsto_t>(m_gstos);
}
public:
static unsigned char* get_rgbas(unsigned int a_w,unsigned int a_h) {
//WARNING : it does OpenGL. Under Android it should be executed
// in the OpenGL thread.
//NOTE : Android, iOS : RGB produces a black image.
unsigned char* rgbas = new unsigned char[4 * a_w * a_h];
if(!rgbas) return 0;
::glPixelStorei(GL_PACK_ALIGNMENT,1); //needed with Cocoa.
::glReadPixels(0,0,a_w,a_h,GL_RGBA,GL_UNSIGNED_BYTE,rgbas);
/*{size_t number = 4 * a_w * a_h;
size_t count_not_255 = 0;
for(size_t item=3;item<number;item+=4) {
unsigned char a = rgbas[item];
if(a!=255) {
::printf("%lu : %d\n",item,a);
count_not_255++;
rgbas[item] = 255;
}
}
::printf("not_255 : %lu\n",count_not_255);}*/
return rgbas;
}
static unsigned char* get_rgbs(unsigned int a_w,unsigned int a_h) {
//WARNING : it does OpenGL. Under Android it should be executed
// in the OpenGL thread.
//NOTE : Android, iOS : RGB produces a black image.
unsigned char* rgbs = new unsigned char[3 * a_w * a_h];
if(!rgbs) return 0;
::glPixelStorei(GL_PACK_ALIGNMENT,1); //needed with Cocoa.
::glReadPixels(0,0,a_w,a_h,GL_RGB,GL_UNSIGNED_BYTE,rgbs);
return rgbs;
}
protected:
std::ostream& m_out;
class gsto_t {
TOOLS_SCLASS(GL_manager::gsto_t)
public:
enum kind {
kind_texture,
kind_buffer,
kind_list,
kind_memory
};
public:
gsto_t(kind a_kind,int a_gl_id,size_t a_size,const float* a_data)
:m_gl_id(a_gl_id)
,m_kind(a_kind)
,m_size(a_size)
,m_data(0)
{
#ifdef TOOLS_MEM
tools::mem::increment(s_class().c_str());
#endif
if(a_data) {
size_t num = m_size/sizeof(float);
m_data = new float[num];
#ifdef TOOLS_MEM
tools::mem::increment(tools::s_new().c_str());
#endif
::memcpy(m_data,a_data,m_size);
}
}
virtual ~gsto_t() {
if(m_kind==kind_texture) {
::glDeleteTextures(1,&m_gl_id);
#ifdef TOOLS_MEM
tools::mem::decrement(tools::s_tex().c_str());
#endif
} else if(m_kind==kind_buffer) {
#ifdef TOOLS_HAS_GL_VBO
::glDeleteBuffers(1,&m_gl_id);
#ifdef TOOLS_MEM
tools::mem::decrement(tools::s_gsto().c_str());
#endif
#endif
} else if(m_kind==kind_list) {
if(m_gl_id) {
#ifdef TOOLS_HAS_GL_LIST
::glDeleteLists(m_gl_id,1);
#ifdef TOOLS_MEM
tools::mem::decrement(tools::s_gsto().c_str());
#endif
#endif
}
}
if(m_data) {
delete [] m_data;
#ifdef TOOLS_MEM
tools::mem::decrement(tools::s_new().c_str());
#endif
}
#ifdef TOOLS_MEM
tools::mem::decrement(s_class().c_str());
#endif
}
private:
gsto_t(const gsto_t& a_from)
:m_gl_id(a_from.m_gl_id)
,m_kind(a_from.m_kind)
,m_size(a_from.m_size)
,m_data(0)
{
#ifdef TOOLS_MEM
tools::mem::increment(s_class().c_str());
#endif
if(a_from.m_data) {
size_t num = m_size/sizeof(float);
m_data = new float[num];
#ifdef TOOLS_MEM
tools::mem::increment(tools::s_new().c_str());
#endif
::memcpy(m_data,a_from.m_data,m_size);
}
}
gsto_t& operator=(const gsto_t& a_from){
if(&a_from==this) return *this;
m_gl_id = a_from.m_gl_id;
m_kind = a_from.m_kind;
m_size = a_from.m_size;
if(m_data) {
delete [] m_data;
#ifdef TOOLS_MEM
tools::mem::decrement(tools::s_new().c_str());
#endif
m_data = 0;
}
if(a_from.m_data) {
size_t num = m_size/sizeof(float);
m_data = new float[num];
#ifdef TOOLS_MEM
tools::mem::increment(tools::s_new().c_str());
#endif
::memcpy(m_data,a_from.m_data,m_size);
}
return *this;
}
public:
bool is_valid() const {
if(m_kind==kind_texture) {
return (::glIsTexture(m_gl_id)==GL_TRUE?true:false);
} else if(m_kind==kind_buffer) {
#ifdef TOOLS_HAS_GL_VBO
return (::glIsBuffer(m_gl_id)==GL_TRUE?true:false);
#endif
} else if(m_kind==kind_list) {
#ifdef TOOLS_HAS_GL_LIST
return (::glIsList(m_gl_id)==GL_TRUE?true:false);
#endif
} else if(m_kind==kind_memory) {
return true;
}
return false;
}
void bind() const {
if(m_kind==kind_texture) {
::glBindTexture(GL_TEXTURE_2D,m_gl_id);
} else if(m_kind==kind_buffer) {
#ifdef TOOLS_HAS_GL_VBO
::glBindBuffer(GL_ARRAY_BUFFER,m_gl_id);
#endif
}
}
public:
unsigned int m_gl_id;
kind m_kind;
size_t m_size;
float* m_data;
};
std::map<unsigned int,gsto_t*> m_gstos;
bool m_gsto_on_card;
unsigned int m_gen_id;
tools::sg::gsto_mode m_gsto_mode;
bool m_warned;
};
}}
#endif