Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:49:38

0001 #pragma once
0002 /**
0003 SGLFW_Program.h : compile and link OpenGL pipeline using shader sources loaded from directory
0004 ===============================================================================================
0005 
0006 
0007 **/
0008 #include "ssys.h"
0009 
0010 
0011 struct SGLFW_Program
0012 {
0013     static constexpr const char* _LEVEL = "SGLFW_Program__LEVEL" ;
0014     static constexpr const char* MVP_KEYS = "ModelViewProjection,MVP" ;
0015 
0016     int level ;
0017     const char* dir ;
0018     const char* vtx_attname ;
0019     const char* nrm_attname ;
0020     const char* ins_attname ;
0021     const char* mvp_uniname ;
0022 
0023     const char* vertex_shader_text ;
0024     const char* geometry_shader_text ;
0025     const char* fragment_shader_text ;
0026 
0027     GLuint program ;
0028     GLint  mvp_location ;
0029     const float* mvp ;
0030     bool dump ;
0031 
0032     SGLFW_Program(
0033         const char* _dir,
0034         const char* _vtx_attname,
0035         const char* _nrm_attname,
0036         const char* _ins_attname,
0037         const char* _mvp_uniname,
0038         const float* _mvp
0039         );
0040     void init();
0041 
0042     void createFromDir(const char* _dir);
0043     void createFromText(const char* vertex_shader_text, const char* geometry_shader_text, const char* fragment_shader_text );
0044     void use() const ;
0045 
0046     GLint getUniformLocation(const char* name) const ;
0047     GLint getAttribLocation(const char* name) const ;
0048 
0049     GLint findUniformLocation(const char* keys, char delim ) const ;
0050     void locateMVP(const char* key, const float* mvp );
0051     void updateMVP() const ;  // called from renderloop_head
0052 
0053     static void UniformMatrix4fv( GLint loc, const float* vv, bool dump );
0054     static void Uniform4fv(       GLint loc, const float* vv, bool dump );
0055 
0056     void enableVertexAttribArray( const char* name, const char* spec, bool dump=false ) const ;
0057     void enableVertexAttribArray_OfTransforms( const char* name ) const ;
0058 
0059     static void Print_shader_info_log(unsigned id);
0060 
0061     template<typename T>
0062     static std::string Desc(const T* tt, int num);
0063 
0064 };
0065 
0066 inline SGLFW_Program::SGLFW_Program(
0067     const char* _dir,
0068     const char* _vtx_attname,
0069     const char* _nrm_attname,
0070     const char* _ins_attname,
0071     const char* _mvp_uniname,
0072     const float* _mvp
0073     )
0074     :
0075     level(ssys::getenvint(_LEVEL, 0)),
0076     dir( _dir ? strdup(_dir) : nullptr ),
0077     vtx_attname( _vtx_attname ? strdup(_vtx_attname) : nullptr ),
0078     nrm_attname( _nrm_attname ? strdup(_nrm_attname) : nullptr ),
0079     ins_attname( _ins_attname ? strdup(_ins_attname) : nullptr ),
0080     mvp_uniname( _mvp_uniname ? strdup(_mvp_uniname) : nullptr ),
0081     vertex_shader_text(nullptr),
0082     geometry_shader_text(nullptr),
0083     fragment_shader_text(nullptr),
0084     program(0),
0085     mvp_location(-1),
0086     mvp(_mvp),
0087     dump(false)
0088 {
0089     init();
0090 }
0091 
0092 
0093 inline void SGLFW_Program::init()
0094 {
0095     if(dir) createFromDir(dir) ;
0096     use();
0097     if(mvp_uniname)
0098     {
0099         mvp_location = getUniformLocation(mvp_uniname);
0100         assert( mvp_location > -1 );
0101     }
0102 }
0103 
0104 
0105 /**
0106 SGLFW_Program::locateMVP
0107 -------------------------
0108 
0109 Does not update GPU side, invoke SGLFW_Program::locateMVP
0110 prior to the renderloop after shader program is
0111 setup and the GLM maths has been instanciated
0112 hence giving the pointer to the world2clip matrix
0113 address.
0114 
0115 **/
0116 
0117 inline void SGLFW_Program::locateMVP(const char* key, const float* mvp_ )
0118 {
0119     if(level > 0) std::cout << "SGLFW_Program::locateMVP backwards compat" << std::endl ;
0120     mvp_location = getUniformLocation(key);
0121     assert( mvp_location > -1 );
0122     mvp = mvp_ ;
0123 }
0124 
0125 
0126 /**
0127 SGLFW_Program::updateMVP
0128 -------------------------
0129 
0130 When mvp_location is > -1 this is called from
0131 the end of renderloop_head so any matrix updates
0132 need to be done before then.
0133 
0134 HMM: could just pass in the pointer ?
0135 
0136 **/
0137 
0138 inline void SGLFW_Program::updateMVP() const
0139 {
0140     if( mvp_location <= -1 ) return ;
0141     assert( mvp != nullptr );
0142     UniformMatrix4fv(mvp_location, mvp, dump );
0143 }
0144 
0145 
0146 
0147 /**
0148 SGLFW_Program::createFromDir
0149 -------------------------------
0150 
0151 Loads {vert/geom/frag}.glsl shader source files from provided directory
0152 and invokes createFromText
0153 
0154 **/
0155 
0156 inline void SGLFW_Program::createFromDir(const char* _dir)
0157 {
0158     const char* dir = U::Resolve(_dir);
0159 
0160     if(level > 0) std::cout
0161         << "SGLFW_Program::createFromDir"
0162         << " _dir " << ( _dir ? _dir : "-" )
0163         << " dir "  << (  dir ?  dir : "-" )
0164         << "\n"
0165         ;
0166 
0167 
0168     vertex_shader_text = U::ReadString(dir, "vert.glsl");
0169     geometry_shader_text = U::ReadString(dir, "geom.glsl");
0170     fragment_shader_text = U::ReadString(dir, "frag.glsl");
0171 
0172     if(level > 0) std::cout
0173         << "SGLFW_Program::createFromDir"
0174         << " _dir " << ( _dir ? _dir : "-" )
0175         << " dir "  << (  dir ?  dir : "-" )
0176         << " vertex_shader_text " << ( vertex_shader_text ? "YES" : "NO" )
0177         << " geometry_shader_text " << ( geometry_shader_text ? "YES" : "NO" )
0178         << " fragment_shader_text " << ( fragment_shader_text ? "YES" : "NO" )
0179         << std::endl
0180         ;
0181 
0182     createFromText( vertex_shader_text, geometry_shader_text, fragment_shader_text );
0183 }
0184 
0185 
0186 /**
0187 SGLFW_Program::createFromText
0188 ------------------------------
0189 
0190 Compiles and links shader strings into a program referred from integer *program*
0191 
0192 On macOS with the below get "runtime error, unsupported version"::
0193 
0194     #version 460 core
0195 
0196 On macOS with the below::
0197 
0198     #version 410 core
0199 
0200 note that a trailing semicolon after the main curly brackets gives a syntax error,
0201 that did not see on Linux with "#version 460 core"
0202 
0203 **/
0204 
0205 inline void SGLFW_Program::createFromText(const char* vertex_shader_text, const char* geometry_shader_text, const char* fragment_shader_text )
0206 {
0207     if(level > 0) std::cout << "[SGLFW_Program::createFromText level " << level << std::endl ;
0208     if(level > 1) std::cout << " vertex_shader_text " << std::endl << ( vertex_shader_text ? vertex_shader_text : "-" ) << std::endl ;
0209     if(level > 1) std::cout << " geometry_shader_text " << std::endl << ( geometry_shader_text ? geometry_shader_text : "-" )  << std::endl ;
0210     if(level > 1) std::cout << " fragment_shader_text " << std::endl << ( fragment_shader_text ? fragment_shader_text : "-" ) << std::endl ;
0211 
0212     bool expect = vertex_shader_text && fragment_shader_text ;
0213     assert( expect );
0214     if(!expect) std::raise(SIGINT);
0215 
0216 
0217     int params = -1;
0218     GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);                    SGLFW__check(__FILE__, __LINE__);
0219     glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);                SGLFW__check(__FILE__, __LINE__);
0220     glCompileShader(vertex_shader);                                             SGLFW__check(__FILE__, __LINE__);
0221     glGetShaderiv (vertex_shader, GL_COMPILE_STATUS, &params);
0222     if (GL_TRUE != params) Print_shader_info_log(vertex_shader) ;
0223 
0224     GLuint geometry_shader = 0 ;
0225     if( geometry_shader_text )
0226     {
0227         geometry_shader = glCreateShader(GL_GEOMETRY_SHADER);                       SGLFW__check(__FILE__, __LINE__);
0228         glShaderSource(geometry_shader, 1, &geometry_shader_text, NULL);            SGLFW__check(__FILE__, __LINE__);
0229         glCompileShader(geometry_shader);                                           SGLFW__check(__FILE__, __LINE__);
0230         glGetShaderiv (geometry_shader, GL_COMPILE_STATUS, &params);
0231         if (GL_TRUE != params) Print_shader_info_log(geometry_shader) ;
0232     }
0233 
0234     GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);                SGLFW__check(__FILE__, __LINE__);
0235     glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);            SGLFW__check(__FILE__, __LINE__);
0236     glCompileShader(fragment_shader);                                           SGLFW__check(__FILE__, __LINE__);
0237     glGetShaderiv (fragment_shader, GL_COMPILE_STATUS, &params);
0238     if (GL_TRUE != params) Print_shader_info_log(fragment_shader) ;
0239 
0240     program = glCreateProgram();               SGLFW__check(__FILE__, __LINE__);
0241     glAttachShader(program, vertex_shader);    SGLFW__check(__FILE__, __LINE__);
0242     if( geometry_shader > 0 )
0243     {
0244         glAttachShader(program, geometry_shader); SGLFW__check(__FILE__, __LINE__);
0245     }
0246     glAttachShader(program, fragment_shader);  SGLFW__check(__FILE__, __LINE__);
0247     glLinkProgram(program);                    SGLFW__check(__FILE__, __LINE__);
0248 
0249     if(level > 0) std::cout << "]SGLFW_Program::createFromText level " << level << std::endl ;
0250 }
0251 
0252 inline void SGLFW_Program::use() const
0253 {
0254     glUseProgram(program);
0255 }
0256 
0257 
0258 /**
0259 SGLFW_Program::getUniformLocation
0260 ----------------------------------
0261 
0262 Caution OpenGL compilation will optimize away Uniforms
0263 that are not used.
0264 
0265 **/
0266 
0267 inline GLint SGLFW_Program::getUniformLocation(const char* name) const
0268 {
0269     GLint loc = glGetUniformLocation(program, name);   SGLFW__check(__FILE__, __LINE__, name, loc, "glGetUniformLocation/m" );
0270     return loc ;
0271 }
0272 
0273 inline GLint SGLFW_Program::getAttribLocation(const char* name) const
0274 {
0275     GLint loc = glGetAttribLocation(program, name);   SGLFW__check(__FILE__, __LINE__, name, loc, "glGetAttribLocation/m" );
0276     return loc ;
0277 }
0278 
0279 
0280 
0281 
0282 /**
0283 SGLFW_Program::findUniformLocation
0284 -----------------------------------
0285 
0286 Returns the location int for the first uniform key found in the
0287 shader program
0288 
0289 **/
0290 
0291 inline GLint SGLFW_Program::findUniformLocation(const char* keys, char delim ) const
0292 {
0293     std::vector<std::string> kk ;
0294 
0295     std::stringstream ss;
0296     ss.str(keys)  ;
0297     std::string key;
0298     while (std::getline(ss, key, delim)) kk.push_back(key) ;
0299 
0300     GLint loc = -1 ;
0301 
0302     int num_key = kk.size();
0303     for(int i=0 ; i < num_key ; i++)
0304     {
0305         const char* k = kk[i].c_str();
0306         loc = getUniformLocation(k);
0307         if(loc > -1) break ;
0308     }
0309     return loc ;
0310 }
0311 
0312 
0313 
0314 template<typename T>
0315 inline std::string SGLFW_Program::Desc(const T* tt, int num) // static
0316 {
0317     std::stringstream ss ;
0318     for(int i=0 ; i < num ; i++)
0319         ss
0320             << ( i % 4 == 0 && num > 4 ? ".\n" : "" )
0321             << " " << std::fixed << std::setw(10) << std::setprecision(4) << tt[i]
0322             << ( i == num-1 && num > 4 ? ".\n" : "" )
0323             ;
0324 
0325     std::string s = ss.str();
0326     return s ;
0327 }
0328 
0329 inline void SGLFW_Program::UniformMatrix4fv( GLint loc, const float* vv, bool dump ) // static
0330 {
0331     if(dump) std::cout
0332         << "SGLFW_Program::UniformMatrix4fv"
0333         << " loc " << loc
0334         << std::endl
0335         << Desc(vv, 16)
0336         << std::endl
0337         ;
0338 
0339     assert( loc > -1 );
0340     glUniformMatrix4fv(loc, 1, GL_FALSE, (const GLfloat*)vv );
0341 }
0342 
0343 inline void SGLFW_Program::Uniform4fv( GLint loc, const float* vv, bool dump ) // static
0344 {
0345     if(dump) std::cout
0346         << "SGLFW_Program::Uniform4fv"
0347         << " loc " << loc
0348         << std::endl
0349         << Desc(vv, 4)
0350         << std::endl
0351         ;
0352 
0353     assert( loc > -1 );
0354     glUniform4fv(loc, 1, (const GLfloat*)vv );
0355 }
0356 
0357 
0358 
0359 
0360 
0361 /**
0362 SGLFW_Program::enableVertexAttribArray
0363 -------------------------------------------
0364 
0365 Array attribute : connecting values from the array with attribute symbol in the shader program
0366 
0367 Example::
0368 
0369     name:"rpos"
0370     spec:"4,GL_FLOAT,GL_FALSE,64,0,false"
0371 
0372 NB when handling multiple buffers note that glVertexAttribPointer
0373 binds to the buffer object bound to GL_ARRAY_BUFFER when called.
0374 So that means have to repeatedly call this again after switching
0375 buffers ?
0376 
0377 * https://stackoverflow.com/questions/14249634/opengl-vaos-and-multiple-buffers
0378 * https://antongerdelan.net/opengl/vertexbuffers.html
0379 
0380 * NOTICE THAT index 0 IS NOT "NULL" for VertexAttribArray
0381 
0382 **/
0383 
0384 inline void SGLFW_Program::enableVertexAttribArray( const char* name, const char* spec, bool dump ) const
0385 {
0386     if(dump) std::cout << "SGLFW_Program::enableVertexAttribArray name [" << name << "]" <<  std::endl ;
0387 
0388     SGLFW_Attrib att(name, spec);
0389 
0390     att.index = getAttribLocation( name );     SGLFW__check(__FILE__, __LINE__, name, att.index, "getAttribLocation" );
0391 
0392     if(dump) std::cout << "SGLFW_Program::enableVertexAttribArray att.desc [" << att.desc() << "]" <<  std::endl ;
0393 
0394     glEnableVertexAttribArray(att.index);      SGLFW__check(__FILE__, __LINE__, name, att.index, "glEnableVertexAttribArray" );
0395 
0396     assert( att.integer_attribute == false );
0397 
0398     glVertexAttribPointer(att.index, att.size, att.type, att.normalized, att.stride, att.byte_offset_pointer );     SGLFW__check(__FILE__, __LINE__, name, att.index, "glVertexAttribPointer" );
0399 }
0400 
0401 
0402 inline void SGLFW_Program::enableVertexAttribArray_OfTransforms( const char* name ) const
0403 {
0404     assert( name );
0405 
0406     SGLFW_Attrib att(name, SMesh::MATROW_SPEC );
0407 
0408     att.index = getAttribLocation( name );     SGLFW__check(__FILE__, __LINE__,name, att.index, "getAttribLocation");
0409 
0410     size_t qsize = att.stride/4 ;
0411     GLuint divisor = 1 ;
0412     // number of instances between updates of attribute , >1 will land that many instances on top of each other
0413 
0414     const void* offset0 = (void*)(qsize*0) ;
0415     const void* offset1 = (void*)(qsize*1) ;
0416     const void* offset2 = (void*)(qsize*2) ;
0417     const void* offset3 = (void*)(qsize*3) ;
0418 
0419     glEnableVertexAttribArray(att.index+0);                                                       SGLFW__check(__FILE__, __LINE__,name,att.index+0, "glEnableVertexAttribArray");
0420     glVertexAttribPointer(att.index+0, att.size, att.type, att.normalized, att.stride, offset0 ); SGLFW__check(__FILE__, __LINE__,name,att.index+0, "glVertexAttribPointer");
0421     glVertexAttribDivisor(att.index+0, divisor);                                                  SGLFW__check(__FILE__, __LINE__,name,att.index+0, "glVertexAttribDivisor");
0422 
0423     glEnableVertexAttribArray(att.index+1);                                                       SGLFW__check(__FILE__, __LINE__,name,att.index+1, "glEnableVertexAttribArray");
0424     glVertexAttribPointer(att.index+1, att.size, att.type, att.normalized, att.stride, offset1 ); SGLFW__check(__FILE__, __LINE__,name,att.index+1, "glVertexAttribPointer");
0425     glVertexAttribDivisor(att.index+1, divisor);                                                  SGLFW__check(__FILE__, __LINE__,name,att.index+1, "glVertexAttribDivisor");
0426 
0427     glEnableVertexAttribArray(att.index+2);                                                       SGLFW__check(__FILE__, __LINE__,name,att.index+2, "glEnableVertexAttribArray");
0428     glVertexAttribPointer(att.index+2, att.size, att.type, att.normalized, att.stride, offset2 ); SGLFW__check(__FILE__, __LINE__,name,att.index+2, "glVertexAttribPointer");
0429     glVertexAttribDivisor(att.index+2, divisor);                                                  SGLFW__check(__FILE__, __LINE__,name,att.index+2, "glVertexAttribDivisor");
0430 
0431     glEnableVertexAttribArray(att.index+3);                                                       SGLFW__check(__FILE__, __LINE__,name,att.index+3, "glEnableVertexAttribArray");
0432     glVertexAttribPointer(att.index+3, att.size, att.type, att.normalized, att.stride, offset3 ); SGLFW__check(__FILE__, __LINE__,name,att.index+3, "glVertexAttribPointer");
0433     glVertexAttribDivisor(att.index+3, divisor);                                                  SGLFW__check(__FILE__, __LINE__,name,att.index+3, "glVertexAttribDivisor");
0434 }
0435 
0436 inline void SGLFW_Program::Print_shader_info_log(unsigned id)  // static
0437 {
0438     int max_length = 2048;
0439     int actual_length = 0;
0440     char log[2048];
0441 
0442     glGetShaderInfoLog(id, max_length, &actual_length, log);
0443     SGLFW__check(__FILE__, __LINE__ );
0444 
0445     printf("SGLFW_Program::Print_shader_info_log GL index %u:\n%s\n", id, log);
0446     assert(0);
0447 }
0448 
0449