├── README.md └── gl.hpp /README.md: -------------------------------------------------------------------------------- 1 | gl.hpp 2 | ==== 3 | 4 | A C\+\+ toolbox for modern OpenGL 3.x/4.x programming. 5 | 6 | This single header file provides light-weight wrappers for OpenGL functionalities. They make using OpenGL easier but do not require the overhead of a full 3D engine. 7 | 8 | Currently provided: 9 | * shaders: Simplifies in-source writing, loading, compiling and linking of OpenGL shaders 10 | * uniforms: Use Eigen types, std::vector and std::array to set and get uniforms 11 | * textures: Load images into OpenGL textures 12 | * buffer objects: Manage buffer objects which can for example hold vertex data. 13 | 14 | I plan to use gl.hpp for the [Ludum Dare 48h game competition](http://www.ludumdare.com/compo/). 15 | To the Ludum Dare folks: Feel free to try pastry and give me feedback :) 16 | 17 | Runs and compiles well under Ubuntu 14.04 64-bit. Also runs under Windows 7 64-bit using mingw-w64 as cross compiler. 18 | 19 | Installation 20 | ---- 21 | 22 | Just copy gl.hpp to your include directory. 23 | 24 | Requirements: 25 | 26 | * C++11 compatible compiler (e.g. gcc 4.8). 27 | * OpenGL 3.3 / GLSL 1.5 compatible graphics card or better 28 | 29 | License 30 | ---- 31 | 32 | Copyright (c) 2014 David Weikersdorfer 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in 42 | all copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 50 | THE SOFTWARE. 51 | 52 | -------------------------------------------------------------------------------- /gl.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 David Weikersdorfer 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #ifndef INCLUDED_PASTRY_PASTRYGL_HPP 22 | #define INCLUDED_PASTRY_PASTRYGL_HPP 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #define PASTRY_GLSL(src) "#version 150\n" #src 36 | 37 | namespace danvil { 38 | namespace pastry 39 | { 40 | typedef GLuint glid_t; 41 | 42 | enum class rid 43 | { 44 | buffer, 45 | vertex_shader, 46 | geometry_shader, 47 | fragment_shader, 48 | program, 49 | vertex_array, 50 | texture_base, 51 | renderbuffer, 52 | framebuffer 53 | }; 54 | 55 | namespace detail 56 | { 57 | inline const char* name(rid r) 58 | { 59 | #define PASTRY_RESOURCE_NAME(T) case rid::T: return #T; 60 | switch(r) { 61 | PASTRY_RESOURCE_NAME(buffer) 62 | PASTRY_RESOURCE_NAME(vertex_shader) 63 | PASTRY_RESOURCE_NAME(geometry_shader) 64 | PASTRY_RESOURCE_NAME(fragment_shader) 65 | PASTRY_RESOURCE_NAME(program) 66 | PASTRY_RESOURCE_NAME(vertex_array) 67 | PASTRY_RESOURCE_NAME(texture_base) 68 | PASTRY_RESOURCE_NAME(renderbuffer) 69 | PASTRY_RESOURCE_NAME(framebuffer) 70 | } 71 | #undef PASTRY_RESOURCE_NAME 72 | } 73 | } 74 | 75 | inline std::ostream& operator<<(std::ostream& os, rid r) 76 | { os << detail::name(r); return os; } 77 | 78 | namespace detail 79 | { 80 | template struct handler; 81 | 82 | template<> struct handler 83 | { 84 | static glid_t gl_create() { glid_t id; glGenBuffers(1, &id); return id; } 85 | static void gl_delete(glid_t id) { glDeleteBuffers(1, &id); } 86 | }; 87 | 88 | template<> struct handler 89 | { 90 | static glid_t gl_create() { return glCreateShader(GL_VERTEX_SHADER); } 91 | static void gl_delete(glid_t id) { glDeleteShader(id); } 92 | }; 93 | 94 | template<> struct handler 95 | { 96 | static glid_t gl_create() { return glCreateShader(GL_GEOMETRY_SHADER); } 97 | static void gl_delete(glid_t id) { glDeleteShader(id); } 98 | }; 99 | 100 | template<> struct handler 101 | { 102 | static glid_t gl_create() { return glCreateShader(GL_FRAGMENT_SHADER); } 103 | static void gl_delete(glid_t id) { glDeleteShader(id); } 104 | }; 105 | 106 | template<> struct handler 107 | { 108 | static glid_t gl_create() { return glCreateProgram(); } 109 | static void gl_delete(glid_t id) { glDeleteProgram(id); } 110 | }; 111 | 112 | template<> struct handler 113 | { 114 | static glid_t gl_create() { glid_t id; glGenVertexArrays(1, &id); return id; } 115 | static void gl_delete(glid_t id) { glDeleteVertexArrays(1, &id); } 116 | }; 117 | 118 | template<> struct handler 119 | { 120 | static glid_t gl_create() { glid_t id; glGenTextures(1, &id); return id; } 121 | static void gl_delete(glid_t id) { glDeleteTextures(1, &id); } 122 | }; 123 | 124 | template<> struct handler 125 | { 126 | static glid_t gl_create() { glid_t id; glGenRenderbuffers(1, &id); return id; } 127 | static void gl_delete(glid_t id) { glDeleteRenderbuffers(1, &id); } 128 | }; 129 | 130 | template<> struct handler 131 | { 132 | static glid_t gl_create() { glid_t id; glGenFramebuffers(1, &id); return id; } 133 | static void gl_delete(glid_t id) { glDeleteFramebuffers(1, &id); } 134 | }; 135 | 136 | constexpr glid_t INVALID_ID = 0; 137 | 138 | template 139 | class resource_base 140 | { 141 | private: 142 | glid_t id_; 143 | 144 | public: 145 | resource_base() 146 | : id_(handler::gl_create()) {} 147 | 148 | resource_base(glid_t id) 149 | : id_(id) {} 150 | 151 | resource_base(const resource_base&) = delete; 152 | resource_base& operator=(const resource_base&) = delete; 153 | 154 | ~resource_base() 155 | { handler::gl_delete(id_); } 156 | 157 | glid_t id() const 158 | { return id_; } 159 | }; 160 | 161 | // class ressource_not_initialized : public std::runtime_error 162 | // { 163 | // public: 164 | // ressource_not_initialized(rid r) 165 | // : std::runtime_error( 166 | // (std::string("pastry: resource of type ") + detail::name(r) + " is not initialized!").c_str()) {} 167 | // }; 168 | 169 | template 170 | struct resource 171 | { 172 | private: 173 | std::shared_ptr> ptr_; 174 | 175 | public: 176 | resource() 177 | : ptr_(std::make_shared>()) {} 178 | 179 | resource(glid_t id) 180 | : ptr_(std::make_shared>(id)) {} 181 | 182 | glid_t id() const 183 | { return ptr_->id(); } 184 | 185 | operator glid_t() const 186 | { return ptr_->id(); } 187 | }; 188 | } 189 | 190 | struct exception 191 | : public std::runtime_error 192 | { 193 | exception(const std::string& msg) 194 | : std::runtime_error(msg.c_str()) 195 | { 196 | std::cerr << "--------------------------------------------------------------------------------" << std::endl; 197 | std::cerr << "-------- PASTRY ERROR --------------------------------------------------------" << std::endl; 198 | std::cerr << "--------------------------------------------------------------------------------" << std::endl; 199 | std::cerr << msg << std::endl; 200 | std::cerr << "--------------------------------------------------------------------------------" << std::endl; 201 | } 202 | }; 203 | 204 | struct invalid_shader_source 205 | : public exception 206 | { 207 | invalid_shader_source(const std::string& source) 208 | : exception(std::string("pastry: invalid shader source:\n") + source) 209 | { } 210 | }; 211 | 212 | struct invalid_shader_program 213 | : public exception 214 | { 215 | invalid_shader_program() 216 | : exception("pastry: invalide shader program") 217 | { } 218 | }; 219 | 220 | struct file_not_found 221 | : public exception 222 | { 223 | file_not_found(const std::string& fn) 224 | : exception(std::string("pastry: file not found: '") + fn + "'") 225 | { } 226 | }; 227 | 228 | namespace detail 229 | { 230 | inline bool can_read_file(const std::string& filename) 231 | { 232 | std::ifstream in(filename, std::ios::in | std::ios::binary); 233 | return in; 234 | } 235 | 236 | inline std::string load_text_file(const std::string& filename) 237 | { 238 | // http://insanecoding.blogspot.de/2011/11/how-to-read-in-file-in-c.html 239 | std::ifstream in(filename, std::ios::in | std::ios::binary); 240 | if(!in) { 241 | throw file_not_found(filename); 242 | } 243 | std::string contents; 244 | in.seekg(0, std::ios::end); 245 | contents.resize(in.tellg()); 246 | in.seekg(0, std::ios::beg); 247 | in.read(&contents[0], contents.size()); 248 | in.close(); 249 | return contents; 250 | } 251 | 252 | inline char int_to_char(unsigned i) 253 | { 254 | if(i == 0 || i > 9) 255 | return '0'; 256 | else 257 | return '1' + i - 1; 258 | } 259 | 260 | inline std::string int_to_string(unsigned i) 261 | { 262 | std::string r = ""; 263 | while(i > 0) { 264 | r = int_to_char(i % 10) + r; 265 | i /= 10; 266 | } 267 | if(r.length() < 3) { 268 | r.insert(0, 3 - r.length(), '0'); 269 | } 270 | return r; 271 | } 272 | 273 | inline void compile_shader(glid_t q, std::string source) 274 | { 275 | // prepare by replacing "; " with ";\n" 276 | // this is to get better compiler error messages 277 | { 278 | size_t index = 0; 279 | while(true) { 280 | index = source.find("; ", index); 281 | if(index == std::string::npos) break; 282 | source.replace(index, 2, ";\n"); 283 | index += 2; 284 | } 285 | } 286 | // compile 287 | GLint len = source.size(); 288 | const GLchar* str = source.data(); 289 | glShaderSource(q, 1, &str, &len); 290 | glCompileShader(q); 291 | // check 292 | GLint status; 293 | glGetShaderiv(q, GL_COMPILE_STATUS, &status); 294 | if(status != GL_TRUE) { 295 | // annotate code with line numbers 296 | { 297 | int i = 2; // first line ist done manually 298 | source = "001: " + source; 299 | size_t index = 0; 300 | while(true) { 301 | index = source.find("\n", index); 302 | if(index == std::string::npos) break; 303 | source.replace(index, 1, "\n" + int_to_string(i++) + ": "); 304 | index += 6; 305 | } 306 | } 307 | // get error message 308 | char buffer[1024]; 309 | glGetShaderInfoLog(q, 1024, NULL, buffer); 310 | // throw exception 311 | throw invalid_shader_source(source + "\n" + buffer); 312 | } 313 | } 314 | } 315 | 316 | struct vertex_shader 317 | : public detail::resource 318 | { 319 | vertex_shader() {} 320 | 321 | vertex_shader(const std::string& source) 322 | { compile(source); } 323 | 324 | void compile(const std::string& source) 325 | { detail::compile_shader(id(), source); } 326 | }; 327 | 328 | struct geometry_shader 329 | : public detail::resource 330 | { 331 | geometry_shader() {} 332 | 333 | geometry_shader(const std::string& source) 334 | { compile(source); } 335 | 336 | void compile(const std::string& source) 337 | { detail::compile_shader(id(), source); } 338 | }; 339 | 340 | struct fragment_shader 341 | : public detail::resource 342 | { 343 | fragment_shader() {} 344 | 345 | fragment_shader(const std::string& source) 346 | { compile(source); } 347 | 348 | void compile(const std::string& source) 349 | { detail::compile_shader(id(), source); } 350 | }; 351 | 352 | template 353 | T load_shader(const std::string& filename) 354 | { return T{detail::load_text_file(filename)}; } 355 | 356 | struct vertex_attribute; 357 | 358 | template struct uniform; 359 | 360 | struct program 361 | : public detail::resource 362 | { 363 | program() {} 364 | 365 | program(const vertex_shader& vs, const fragment_shader& fs) 366 | { 367 | attach(vs); 368 | attach(fs); 369 | link(); 370 | } 371 | 372 | program(const vertex_shader& vs, const geometry_shader& gs, const fragment_shader& fs) 373 | { 374 | attach(vs); 375 | attach(gs); 376 | attach(fs); 377 | link(); 378 | } 379 | 380 | void attach(const vertex_shader& s) 381 | { glAttachShader(id(), s.id()); } 382 | 383 | void attach(const geometry_shader& s) 384 | { glAttachShader(id(), s.id()); } 385 | 386 | void attach(const fragment_shader& s) 387 | { glAttachShader(id(), s.id()); } 388 | 389 | void link() 390 | { 391 | glLinkProgram(id()); 392 | // check if link was successful 393 | GLint status; 394 | glGetProgramiv(id(), GL_LINK_STATUS, &status); 395 | if(status != GL_TRUE) { 396 | // print error message 397 | char buffer[1024]; 398 | glGetProgramInfoLog(id(), 1024, NULL, buffer); 399 | throw invalid_shader_program(); 400 | } 401 | } 402 | 403 | void use() const 404 | { glUseProgram(id()); } 405 | 406 | static void unuse() 407 | { glUseProgram(detail::INVALID_ID); } 408 | 409 | inline vertex_attribute get_attribute(const std::string& name) const; 410 | 411 | template 412 | uniform get_uniform(const std::string& name) const; 413 | }; 414 | 415 | inline program create_program(const std::string& src_vertex, const std::string& src_frag) 416 | { 417 | return program{ {src_vertex}, {src_frag} }; 418 | } 419 | 420 | inline program create_program(const std::string& src_vertex, const std::string& src_geom, const std::string& src_frag) 421 | { 422 | return program{ {src_vertex}, {src_geom}, {src_frag} }; 423 | } 424 | 425 | inline program load_program(const std::string& fn_vertex, const std::string& fn_frag) 426 | { 427 | return program{ 428 | load_shader(fn_vertex), 429 | load_shader(fn_frag) 430 | }; 431 | } 432 | 433 | inline program load_program(const std::string& fn_vertex, const std::string& fn_geom, const std::string& fn_frag) 434 | { 435 | return program{ 436 | load_shader(fn_vertex), 437 | load_shader(fn_geom), 438 | load_shader(fn_frag) 439 | }; 440 | } 441 | 442 | /** Tries to load a shader with files fn.vert, fn.geom, fn.frag */ 443 | inline program load_program(const std::string& fn) 444 | { 445 | std::string fn_vert = fn + ".vert"; 446 | std::string fn_geom = fn + ".geom"; 447 | std::string fn_frag = fn + ".frag"; 448 | if(detail::can_read_file(fn_geom)) { 449 | return load_program(fn_vert, fn_geom, fn_frag); 450 | } 451 | else { 452 | return load_program(fn_vert, fn_frag); 453 | } 454 | } 455 | 456 | struct vertex_attribute 457 | { 458 | GLint loc; 459 | 460 | bool is_valid() const 461 | { return loc >= 0; } 462 | 463 | void configure(GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer) 464 | { glVertexAttribPointer(loc, size, type, normalized, stride, pointer); } 465 | 466 | void set_divisor(unsigned divisor) 467 | { glVertexAttribDivisor(loc, divisor); } 468 | 469 | void enable() 470 | { glEnableVertexAttribArray(loc); } 471 | }; 472 | 473 | vertex_attribute program::get_attribute(const std::string& name) const 474 | { 475 | vertex_attribute va; 476 | va.loc = glGetAttribLocation(id(), name.data()); 477 | if(va.loc == -1) { 478 | std::cerr << "ERROR: Inactive or invalid vertex attribute '" << name << "'" << std::endl; 479 | } 480 | return va; 481 | } 482 | 483 | namespace detail 484 | { 485 | template 486 | struct uniform_impl; 487 | // { 488 | // static_assert(false, 489 | // "\n\n" 490 | // "ERROR with pastry::uniform / glUniform / glUniformMatrix:\n" 491 | // "\tInvalid row/column dimensions for OpenGL uniform.\n" 492 | // "\tSupported are: 1x1, 2x1, 3x1, 4x1, 2x2, 3x3, 4x4, 2x3, 3x2, 2x4, 4x2, 3x4, 4x3.\n"); 493 | // }; 494 | 495 | // K => glUniform1Kv 496 | // K[N] => glUniformNKv 497 | // Eigen::VectorNK => glUniformNKv 498 | 499 | #define PASTRY_UNIFORM_TYPES_IMPL_VAL(F,N) \ 500 | F(float, N, glUniform##N##fv, glGetUniformfv) \ 501 | F(int, N, glUniform##N##iv, glGetUniformiv) \ 502 | F(unsigned int, N, glUniform##N##uiv, glGetUniformuiv) 503 | 504 | #define PASTRY_UNIFORM_VAL_DEF(TYPE,N,SETVEC,GETVEC) \ 505 | template \ 506 | struct uniform_impl { \ 507 | static void set(glid_t loc, const TYPE* v) { SETVEC(loc, NUM, v); } \ 508 | static void get(glid_t prog, glid_t loc, TYPE* v) { GETVEC(prog, loc, v); } \ 509 | }; 510 | 511 | PASTRY_UNIFORM_TYPES_IMPL_VAL(PASTRY_UNIFORM_VAL_DEF,1) 512 | PASTRY_UNIFORM_TYPES_IMPL_VAL(PASTRY_UNIFORM_VAL_DEF,2) 513 | PASTRY_UNIFORM_TYPES_IMPL_VAL(PASTRY_UNIFORM_VAL_DEF,3) 514 | PASTRY_UNIFORM_TYPES_IMPL_VAL(PASTRY_UNIFORM_VAL_DEF,4) 515 | 516 | // Eigen::MatrixNf => glUniformMatrixNfv 517 | 518 | #define PASTRY_UNIFORM_TYPES_IMPL_MATN(F,N) \ 519 | F(float, N, glUniformMatrix##N##fv, glGetUniformfv) 520 | 521 | #define PASTRY_UNIFORM_MATN_DEF(TYPE,N,SETVEC,GETVEC) \ 522 | template \ 523 | struct uniform_impl { \ 524 | static void set(glid_t loc, const TYPE* v) { SETVEC(loc, NUM, GL_FALSE, v); } \ 525 | static void get(glid_t prog, glid_t loc, TYPE* v) { GETVEC(prog, loc, v); } \ 526 | }; 527 | 528 | PASTRY_UNIFORM_TYPES_IMPL_MATN(PASTRY_UNIFORM_MATN_DEF,2) 529 | PASTRY_UNIFORM_TYPES_IMPL_MATN(PASTRY_UNIFORM_MATN_DEF,3) 530 | PASTRY_UNIFORM_TYPES_IMPL_MATN(PASTRY_UNIFORM_MATN_DEF,4) 531 | 532 | // Eigen::MatrixNf => glUniformMatrixNfv 533 | 534 | #define PASTRY_UNIFORM_TYPES_IMPL_MATRC(F,R,C) \ 535 | F(float, R,C, glUniformMatrix##R##x##C##fv, glGetUniformfv) 536 | 537 | #define PASTRY_UNIFORM_MATRC_DEF(TYPE,R,C,SETVEC,GETVEC) \ 538 | template \ 539 | struct uniform_impl { \ 540 | static void set(glid_t loc, const TYPE* v) { SETVEC(loc, NUM, GL_FALSE, v); } \ 541 | static void get(glid_t prog, glid_t loc, TYPE* v) { GETVEC(prog, loc, v); } \ 542 | }; 543 | 544 | PASTRY_UNIFORM_TYPES_IMPL_MATRC(PASTRY_UNIFORM_MATRC_DEF,2,3) 545 | PASTRY_UNIFORM_TYPES_IMPL_MATRC(PASTRY_UNIFORM_MATRC_DEF,3,2) 546 | PASTRY_UNIFORM_TYPES_IMPL_MATRC(PASTRY_UNIFORM_MATRC_DEF,2,4) 547 | PASTRY_UNIFORM_TYPES_IMPL_MATRC(PASTRY_UNIFORM_MATRC_DEF,4,2) 548 | PASTRY_UNIFORM_TYPES_IMPL_MATRC(PASTRY_UNIFORM_MATRC_DEF,3,4) 549 | PASTRY_UNIFORM_TYPES_IMPL_MATRC(PASTRY_UNIFORM_MATRC_DEF,4,3) 550 | 551 | } 552 | 553 | /* 554 | | - C++ - | - GLSL - 555 | | uniform | float v 556 | | uniform | int v[2] 557 | | uniform | vec3 v[4] 558 | | uniform | mat4 v[2] 559 | */ 560 | 561 | struct invalid_uniform_location 562 | : public exception 563 | { 564 | invalid_uniform_location(const std::string& name) 565 | : exception("pastry: Inactive or invalid uniform location: name=" + name) 566 | {} 567 | }; 568 | 569 | struct uniform_base 570 | { 571 | std::string name; 572 | 573 | GLint loc; 574 | 575 | program spo; 576 | 577 | bool valid() const 578 | { return loc >= 0; } 579 | 580 | protected: 581 | void prepare() const 582 | { 583 | // if(!valid()) { 584 | // throw invalid_uniform_location{name}; 585 | // } 586 | spo.use(); 587 | } 588 | }; 589 | 590 | /** Example: C++ int[2] / GLSL int[2] 591 | * T: uniform type, must be float (GLSL: float), int (GLSL: int) or unsigned int (GLSL: uint) 592 | * NUM>1: length of uniform array (for NUM=1 there is a specialization) 593 | */ 594 | template 595 | struct uniform 596 | : public uniform_base 597 | { 598 | void set(std::initializer_list values_list) 599 | { 600 | if(values_list.size() != NUM) { 601 | std::cerr << "ERROR in uniform::set: Wrong number of arrays!" << std::endl; 602 | return; 603 | } 604 | if(!valid()) { 605 | return; 606 | } 607 | prepare(); 608 | detail::uniform_impl::set(loc, values_list.begin()); 609 | } 610 | 611 | std::array get(glid_t prog_id) 612 | { 613 | std::array a; 614 | if(!valid()) { 615 | return a; 616 | } 617 | prepare(); 618 | detail::uniform_impl::get(prog_id, loc, a.begin()); 619 | return a; 620 | } 621 | }; 622 | 623 | /** Example: C++ int / GLSL int */ 624 | template 625 | struct uniform 626 | : public uniform_base 627 | { 628 | void set(const T& v) 629 | { 630 | if(!valid()) { 631 | return; 632 | } 633 | prepare(); 634 | detail::uniform_impl::set(loc, &v); 635 | } 636 | 637 | T get(glid_t prog_id) 638 | { 639 | T v; 640 | if(!valid()) { 641 | return v; 642 | } 643 | prepare(); 644 | detail::uniform_impl::get(prog_id, loc, &v); 645 | return v; 646 | } 647 | }; 648 | 649 | /** Example: C++ Eigen::Vector3f / GLSL vec3 */ 650 | template 651 | struct uniform,1> : public uniform_base 652 | { 653 | typedef Eigen::Matrix mat_t; 654 | 655 | void set(const mat_t& v) 656 | { 657 | if(!valid()) { 658 | return; 659 | } 660 | prepare(); 661 | detail::uniform_impl::set(loc, v.data()); 662 | } 663 | 664 | mat_t get(glid_t prog_id) 665 | { 666 | mat_t v; 667 | if(!valid()) { 668 | return v; 669 | } 670 | prepare(); 671 | detail::uniform_impl::get(prog_id, loc, v.data()); 672 | return v; 673 | } 674 | }; 675 | 676 | struct invalid_uniform_initializer_list 677 | : public exception 678 | { 679 | invalid_uniform_initializer_list(unsigned expected, unsigned actual) 680 | : exception("pastry: invalid initializer_list for uniform: expected=TODO, actual=TODO") 681 | {} 682 | }; 683 | 684 | /** Example: C++ Eigen::Vector3f[2] / GLSL vec3[2] */ 685 | template 686 | struct uniform,NUM> : public uniform_base 687 | { 688 | typedef Eigen::Matrix mat_t; 689 | 690 | void set(std::initializer_list values_list) 691 | { 692 | if(values_list.size() != NUM) { 693 | throw invalid_uniform_initializer_list{NUM, values_list.list()}; 694 | } 695 | // copy to continuous memory 696 | K buff[R*C*NUM]; 697 | for(unsigned int i=0; i::set(loc, buff); 707 | } 708 | 709 | std::array get(glid_t prog_id) 710 | { 711 | std::array a; 712 | // read from opengl 713 | if(!valid()) { 714 | return; 715 | } 716 | prepare(); 717 | K buff[R*C*NUM]; 718 | detail::uniform_impl::get(prog_id, loc, buff); 719 | // create array 720 | for(unsigned int i=0; i 729 | uniform program::get_uniform(const std::string& name) const 730 | { 731 | uniform u; 732 | u.name = name; 733 | u.spo = *this; 734 | u.loc = glGetUniformLocation(id(), name.data()); 735 | return u; 736 | } 737 | 738 | namespace detail 739 | { 740 | struct va_data 741 | { 742 | std::string name; 743 | GLenum type; 744 | GLint size; 745 | std::size_t bytes_per_element; 746 | std::size_t bytes_total; 747 | std::size_t offset_begin; 748 | std::size_t offset_end; 749 | }; 750 | 751 | struct layout_item 752 | { 753 | std::string name; 754 | GLenum type; 755 | int size; 756 | }; 757 | 758 | inline std::size_t bytes_per_element(GLenum type) 759 | { 760 | switch(type) { 761 | case GL_BYTE: return 1; 762 | case GL_UNSIGNED_BYTE: return 1; 763 | case GL_SHORT: return 2; 764 | case GL_UNSIGNED_SHORT: return 2; 765 | case GL_INT: return 4; 766 | case GL_UNSIGNED_INT: return 4; 767 | case GL_FLOAT: return 4; 768 | case GL_DOUBLE: return 8; 769 | default: return 1; 770 | } 771 | } 772 | 773 | inline std::vector va_conf(const std::vector& list) 774 | { 775 | std::vector q; 776 | q.reserve(list.size()); 777 | for(const layout_item& i : list) { 778 | va_data dat; 779 | dat.name = i.name; 780 | dat.type = i.type; 781 | dat.size = i.size; 782 | dat.bytes_per_element = bytes_per_element(i.type); 783 | dat.bytes_total = dat.size * dat.bytes_per_element; 784 | dat.offset_begin = (q.empty() ? 0 : q.back().offset_end); 785 | dat.offset_end = dat.offset_begin + dat.bytes_total; 786 | q.push_back(dat); 787 | } 788 | return q; 789 | } 790 | 791 | inline std::size_t va_bytes_total(const std::vector& list) 792 | { 793 | std::size_t n = 0; 794 | for(const layout_item& i : list) { 795 | n += i.size * bytes_per_element(i.type); 796 | } 797 | return n; 798 | } 799 | } 800 | 801 | inline detail::layout_item layout_skip_bytes(int a) 802 | { return {"", 0, a}; } 803 | 804 | template 805 | inline detail::layout_item layout_skip() 806 | { return layout_skip_bytes(sizeof(K)*N); } 807 | 808 | template 809 | struct buffer 810 | : public detail::resource 811 | { 812 | private: 813 | std::size_t num_bytes_; 814 | GLuint usage_; 815 | 816 | public: 817 | std::vector layout_; 818 | 819 | buffer() 820 | : num_bytes_(0), usage_(GL_DYNAMIC_DRAW) {} 821 | 822 | buffer(std::initializer_list list) 823 | : num_bytes_(0), usage_(GL_DYNAMIC_DRAW) 824 | { 825 | bind(); 826 | set_layout(list); 827 | } 828 | 829 | buffer(std::initializer_list list, GLuint usage) 830 | : num_bytes_(0), usage_(GL_DYNAMIC_DRAW) 831 | { 832 | bind(); 833 | set_layout(list); 834 | init_data(usage); 835 | } 836 | 837 | buffer(std::initializer_list list, std::size_t num_bytes, GLuint usage) 838 | : num_bytes_(0), usage_(GL_DYNAMIC_DRAW) 839 | { 840 | bind(); 841 | set_layout(list); 842 | init_data(num_bytes, usage); 843 | } 844 | 845 | void set_layout(const std::vector& list) 846 | { layout_ = detail::va_conf(list); } 847 | 848 | void bind() const 849 | { glBindBuffer(TARGET, id()); } 850 | 851 | template 852 | void init_data(const std::vector& v, GLuint usage) 853 | { init_data(v.data(), v.size(), usage); } 854 | 855 | template 856 | void init_data(const T* buf, std::size_t num_elements, GLuint usage) 857 | { init_data(reinterpret_cast(buf), sizeof(T)*num_elements, usage); } 858 | 859 | void init_data(std::size_t num_bytes, GLuint usage) 860 | { init_data(nullptr, num_bytes_, usage); } 861 | 862 | void init_data(GLuint usage) 863 | { init_data(nullptr, 0, usage); } 864 | 865 | template 866 | void update_data(const std::vector& v) 867 | { update_data(v.data(), v.size()); } 868 | 869 | template 870 | void update_data(const T* buf, std::size_t num_elements) 871 | { update_data(reinterpret_cast(buf), sizeof(T)*num_elements); } 872 | 873 | private: 874 | void init_data(const void* buf, std::size_t num_bytes, GLuint usage) 875 | { 876 | usage_ = usage; 877 | num_bytes_ = num_bytes; 878 | bind(); 879 | glBufferData(TARGET, num_bytes_, buf, usage); 880 | } 881 | 882 | void update_data(const void* buf, std::size_t num_bytes) 883 | { 884 | if(num_bytes != num_bytes_) { 885 | init_data(buf, num_bytes, usage_); 886 | } 887 | else { 888 | bind(); 889 | glBufferSubData(TARGET, 0, num_bytes, buf); 890 | } 891 | } 892 | 893 | static void unbind() 894 | { glBindBuffer(TARGET, detail::INVALID_ID); } 895 | }; 896 | 897 | typedef buffer array_buffer; 898 | 899 | typedef buffer element_array_buffer; 900 | 901 | namespace detail 902 | { 903 | struct mapping 904 | { 905 | std::string shader_name; 906 | array_buffer vb; 907 | std::string vb_name; 908 | unsigned divisor; 909 | 910 | mapping() 911 | {} 912 | 913 | mapping(const std::string& nsn, const array_buffer& nvb) 914 | : shader_name(nsn), vb(nvb), vb_name(nsn), divisor(0) 915 | {} 916 | 917 | mapping(const std::string& nsn, const array_buffer& nvb, const std::string& nvbn) 918 | : shader_name(nsn), vb(nvb), vb_name(nvbn), divisor(0) 919 | {} 920 | 921 | mapping(const std::string& nsn, const array_buffer& nvb, const std::string& nvbn, unsigned nd) 922 | : shader_name(nsn), vb(nvb), vb_name(nvbn), divisor(nd) 923 | {} 924 | }; 925 | } 926 | 927 | struct vertex_array 928 | : public detail::resource 929 | { 930 | std::vector attributes_; 931 | 932 | vertex_array() 933 | {} 934 | 935 | vertex_array(const program& p, std::initializer_list list) 936 | { set_layout(p, list.begin(), list.end()); } 937 | 938 | template 939 | void set_layout(const program& p, It kt1, It kt2) 940 | { 941 | bind(); 942 | for(auto kt=kt1; kt!=kt2; ++kt) { 943 | const detail::mapping& m = *kt; 944 | const std::string& shader_name = m.shader_name; 945 | const array_buffer& vb = m.vb; 946 | const std::string& vn_name = m.vb_name; 947 | // find name in array 948 | auto it = std::find_if(vb.layout_.begin(), vb.layout_.end(), 949 | [&vn_name](const detail::va_data& x) { return x.name == vn_name; }); 950 | if(it == vb.layout_.end()) { 951 | std::cerr << "ERROR: Could not find variable '" << vn_name << "' in buffer layout!" << std::endl; 952 | continue; 953 | } 954 | // need to bind array buffer to establish connection between shader and buffer 955 | vb.bind(); 956 | // create vertex attribute 957 | vertex_attribute va = p.get_attribute(shader_name); 958 | // std::cout << "shader_name = " << shader_name << std::endl; 959 | // std::cout << "vb = " << vb << std::endl; 960 | // std::cout << "vn_name = " << vn_name << std::endl; 961 | // std::cout << "size = " << it->size << std::endl; 962 | // std::cout << "type = " << it->type << std::endl; 963 | // std::cout << "stride = " << vb.layout_.back().offset_end << std::endl; 964 | // std::cout << "offset = " << it->offset_begin << std::endl; 965 | // std::cout << "divisor = " << m.divisor << std::endl; 966 | va.configure(it->size, it->type, GL_FALSE, vb.layout_.back().offset_end, (GLvoid*)it->offset_begin); 967 | va.enable(); 968 | va.set_divisor(m.divisor); // for instancing 969 | attributes_.push_back(va); 970 | } 971 | } 972 | 973 | void bind() 974 | { 975 | glBindVertexArray(id()); 976 | } 977 | 978 | }; 979 | 980 | namespace detail 981 | { 982 | template struct mesh_type_traits; 983 | #define MESH_TYPE_DEF(M,NB,NE) \ 984 | template<> struct mesh_type_traits { \ 985 | static constexpr unsigned num_base = NB; \ 986 | static constexpr unsigned num_per_element = NE; \ 987 | }; 988 | MESH_TYPE_DEF(GL_POINTS,0,1) 989 | MESH_TYPE_DEF(GL_LINE_STRIP,1,1) 990 | MESH_TYPE_DEF(GL_LINES,0,2) 991 | MESH_TYPE_DEF(GL_TRIANGLE_STRIP,2,1) 992 | MESH_TYPE_DEF(GL_TRIANGLE_FAN,2,1) 993 | MESH_TYPE_DEF(GL_TRIANGLES,0,3) 994 | 995 | template struct mesh_index_types_traits; 996 | #define INDEX_TYPE_DEF(IT,K) \ 997 | template<> struct mesh_index_types_traits { \ 998 | typedef K result; \ 999 | }; 1000 | INDEX_TYPE_DEF(GL_UNSIGNED_BYTE,uint8_t) 1001 | INDEX_TYPE_DEF(GL_UNSIGNED_SHORT,uint16_t) 1002 | INDEX_TYPE_DEF(GL_UNSIGNED_INT,uint32_t) 1003 | 1004 | template 1005 | struct index { typedef std::array result; }; 1006 | 1007 | template 1008 | struct index { typedef I result; }; 1009 | } 1010 | 1011 | /** Class to hold mesh data 1012 | * Example usage: 1013 | * // mesh data for a quad 1014 | * struct my_vertex { float x,y,z; }; 1015 | * pastry::triangle_mesh mesh; 1016 | * mesh.vertices = { {0,0,0}, {1,0,0}, {1,1,0}, {0,1,0} }; 1017 | * mesh.indices= { {0,1,2}, {0,2,3} }; 1018 | */ 1019 | template 1020 | struct mesh 1021 | { 1022 | typedef detail::mesh_type_traits m_traits; 1023 | 1024 | typedef detail::mesh_index_types_traits i_traits; 1025 | 1026 | typedef typename detail::index< 1027 | typename i_traits::result, 1028 | m_traits::num_per_element>::result I; 1029 | 1030 | std::vector vertices; 1031 | 1032 | std::vector indices; 1033 | 1034 | void draw_arrays() const 1035 | { glDrawArrays(MODE, 0, vertices.size()); } 1036 | 1037 | void draw_arrays_instanced(std::size_t primcount) const 1038 | { glDrawArraysInstanced(MODE, 0, vertices.size(), primcount); } 1039 | 1040 | void draw_elements() const 1041 | { 1042 | std::size_t count = m_traits::num_per_element*indices.size(); 1043 | glDrawElements(MODE, count, INDEX_TYPE, 0); 1044 | } 1045 | 1046 | void draw_elements_instanced(std::size_t primcount) const 1047 | { 1048 | std::size_t count = m_traits::num_per_element*indices.size(); 1049 | glDrawElementsInstanced(MODE, count, INDEX_TYPE, 0, primcount); 1050 | } 1051 | }; 1052 | 1053 | // template 1054 | // using point_mesh = mesh; 1055 | 1056 | // template 1057 | // using line_strip_mesh = mesh; 1058 | 1059 | // template 1060 | // using line_mesh = mesh; 1061 | 1062 | // template 1063 | // using triangle_strip_mesh = mesh; 1064 | 1065 | // template 1066 | // using triangle_fan_mesh = mesh; 1067 | 1068 | // template 1069 | // using triangle_mesh = mesh; 1070 | 1071 | struct single_mesh 1072 | { 1073 | private: 1074 | GLenum mode_; 1075 | GLenum index_type_; 1076 | 1077 | array_buffer vertex_bo_; 1078 | element_array_buffer index_bo_; 1079 | 1080 | std::size_t num_vertices_ = 0; 1081 | std::size_t num_indices_ = 0; 1082 | 1083 | public: 1084 | const array_buffer& get_vertex_bo() const 1085 | { return vertex_bo_; } 1086 | 1087 | const element_array_buffer& get_index_bo() const 1088 | { return index_bo_; } 1089 | 1090 | array_buffer& get_vertex_bo() 1091 | { return vertex_bo_; } 1092 | 1093 | element_array_buffer& get_index_bo() 1094 | { return index_bo_; } 1095 | 1096 | void set_vertex_bo(const array_buffer& o) 1097 | { vertex_bo_ = o; } 1098 | 1099 | void set_index_bo(const element_array_buffer& o) 1100 | { index_bo_ = o; } 1101 | 1102 | public: 1103 | single_mesh() { 1104 | clear(); 1105 | } 1106 | 1107 | single_mesh(GLenum mode) { 1108 | set_mode(mode); 1109 | clear(); 1110 | } 1111 | 1112 | void clear() { 1113 | num_vertices_ = 0; 1114 | num_indices_ = 0; 1115 | } 1116 | 1117 | void set_mode(GLenum mode) { 1118 | mode_ = mode; 1119 | } 1120 | 1121 | template 1122 | void set_vertices(const std::vector& vertices) { 1123 | num_vertices_ = vertices.size(); 1124 | vertex_bo_.update_data(vertices); 1125 | } 1126 | 1127 | void set_indices(const std::vector& indices) { 1128 | index_type_ = GL_UNSIGNED_BYTE; 1129 | num_indices_ = indices.size(); 1130 | index_bo_.update_data(indices); 1131 | } 1132 | 1133 | void set_indices(const std::vector& indices) { 1134 | index_type_ = GL_UNSIGNED_SHORT; 1135 | num_indices_ = indices.size(); 1136 | index_bo_.update_data(indices); 1137 | } 1138 | 1139 | void set_indices(const std::vector& indices) { 1140 | index_type_ = GL_UNSIGNED_INT; 1141 | num_indices_ = indices.size(); 1142 | index_bo_.update_data(indices); 1143 | } 1144 | 1145 | void clear_indices() { 1146 | set_indices(std::vector{}); 1147 | } 1148 | 1149 | void render() { 1150 | if(num_vertices_ == 0) { 1151 | return; 1152 | } 1153 | if(num_indices_ == 0) { 1154 | vertex_bo_.bind(); 1155 | glDrawArrays(mode_, 0, num_vertices_); 1156 | } 1157 | else { 1158 | vertex_bo_.bind(); 1159 | index_bo_.bind(); // bind the index buffer object! 1160 | glDrawElements(mode_, num_indices_, index_type_, 0); 1161 | } 1162 | } 1163 | }; 1164 | 1165 | struct multi_mesh 1166 | { 1167 | private: 1168 | GLenum mode_; 1169 | GLenum index_type_; 1170 | 1171 | array_buffer vertex_bo_; 1172 | element_array_buffer index_bo_; 1173 | array_buffer instance_bo_; 1174 | 1175 | std::size_t num_vertices_; 1176 | std::size_t num_indices_; 1177 | std::size_t num_instances_; 1178 | 1179 | public: 1180 | const array_buffer& get_vertex_bo() const { return vertex_bo_; } 1181 | const element_array_buffer& get_index_bo() const { return index_bo_; } 1182 | const array_buffer& get_instance_bo() const { return instance_bo_; } 1183 | array_buffer& get_vertex_bo() { return vertex_bo_; } 1184 | element_array_buffer& get_index_bo() { return index_bo_; } 1185 | array_buffer& get_instance_bo() { return instance_bo_; } 1186 | void set_vertex_bo(const array_buffer& o) { vertex_bo_ = o; } 1187 | void set_index_bo(const element_array_buffer& o) { index_bo_ = o; } 1188 | void set_instance_bo(const array_buffer& o) { instance_bo_ = o; } 1189 | 1190 | public: 1191 | multi_mesh() { 1192 | clear(); 1193 | } 1194 | 1195 | multi_mesh(GLenum mode) { 1196 | set_mode(mode); 1197 | clear(); 1198 | } 1199 | 1200 | void clear() { 1201 | num_vertices_ = 0; 1202 | num_indices_ = 0; 1203 | num_instances_ = 0; 1204 | } 1205 | 1206 | void set_mode(GLenum mode) { 1207 | mode_ = mode; 1208 | } 1209 | 1210 | template 1211 | void set_vertices(const std::vector& vertices) { 1212 | num_vertices_ = vertices.size(); 1213 | vertex_bo_.update_data(vertices); 1214 | } 1215 | 1216 | void set_indices(const std::vector& indices) { 1217 | index_type_ = GL_UNSIGNED_BYTE; 1218 | num_indices_ = indices.size(); 1219 | index_bo_.update_data(indices); 1220 | } 1221 | 1222 | void set_indices(const std::vector& indices) { 1223 | index_type_ = GL_UNSIGNED_SHORT; 1224 | num_indices_ = indices.size(); 1225 | index_bo_.update_data(indices); 1226 | } 1227 | 1228 | void set_indices(const std::vector& indices) { 1229 | index_type_ = GL_UNSIGNED_INT; 1230 | num_indices_ = indices.size(); 1231 | index_bo_.update_data(indices); 1232 | } 1233 | 1234 | template 1235 | void set_instances(const std::vector& instances) { 1236 | num_instances_ = instances.size(); 1237 | instance_bo_.update_data(instances); 1238 | } 1239 | 1240 | void set_instances_raw(std::size_t num, const std::vector& instances_data) { 1241 | num_instances_ = num; 1242 | instance_bo_.update_data(instances_data); 1243 | } 1244 | 1245 | void render() { 1246 | if(num_vertices_ == 0) { 1247 | return; 1248 | } 1249 | if(num_indices_ == 0) { 1250 | // use glDrawArrays 1251 | if(num_instances_ == 0) { 1252 | glDrawArrays(mode_, 0, num_vertices_); 1253 | } 1254 | else { 1255 | glDrawArraysInstanced(mode_, 0, num_vertices_, num_instances_); 1256 | } 1257 | } 1258 | else { 1259 | // use glDrawElements 1260 | index_bo_.bind(); // bind the index buffer object! 1261 | if(num_instances_ == 0) { 1262 | glDrawElements(mode_, num_indices_, index_type_, 0); 1263 | } 1264 | else { 1265 | glDrawElementsInstanced(mode_, num_indices_, index_type_, 0, num_instances_); 1266 | } 1267 | } 1268 | } 1269 | }; 1270 | 1271 | namespace detail 1272 | { 1273 | template struct texture_format; 1274 | #define TEXTURE_FORMAT(N,V) \ 1275 | template<> struct texture_format { \ 1276 | static constexpr GLint result = V; \ 1277 | }; 1278 | TEXTURE_FORMAT(1, GL_RED) 1279 | TEXTURE_FORMAT(2, GL_RG) 1280 | TEXTURE_FORMAT(3, GL_RGB) 1281 | TEXTURE_FORMAT(4, GL_RGBA) 1282 | #undef TEXTURE_FORMAT 1283 | 1284 | template struct texture_type; 1285 | #define TEXTURE_TYPE(T,V) \ 1286 | template<> struct texture_type { \ 1287 | static constexpr GLint result = V; \ 1288 | }; 1289 | TEXTURE_TYPE(unsigned char, GL_UNSIGNED_BYTE) 1290 | TEXTURE_TYPE(char, GL_BYTE) 1291 | TEXTURE_TYPE(unsigned short, GL_UNSIGNED_SHORT) 1292 | TEXTURE_TYPE(short, GL_SHORT) 1293 | TEXTURE_TYPE(unsigned int, GL_UNSIGNED_INT) 1294 | TEXTURE_TYPE(int, GL_INT) 1295 | TEXTURE_TYPE(float, GL_FLOAT) 1296 | #undef TEXTURE_TYPE 1297 | } 1298 | 1299 | template 1300 | struct texture_base 1301 | : public detail::resource 1302 | { 1303 | static constexpr GLenum target = TARGET; 1304 | 1305 | texture_base() 1306 | {} 1307 | 1308 | texture_base(glid_t tex_id) 1309 | : detail::resource(tex_id) 1310 | {} 1311 | 1312 | void create(GLenum filter, GLenum wrap) 1313 | { 1314 | bind(); 1315 | set_filter(filter); 1316 | set_wrap(wrap); 1317 | } 1318 | 1319 | void create() 1320 | { create(GL_LINEAR, GL_REPEAT); } 1321 | 1322 | void bind() const 1323 | { glBindTexture(target, id()); } 1324 | 1325 | void set_wrap_s(GLint value) 1326 | { glTexParameteri(target, GL_TEXTURE_WRAP_S, value); } 1327 | 1328 | void set_wrap_t(GLint value) 1329 | { glTexParameteri(target, GL_TEXTURE_WRAP_T, value); } 1330 | 1331 | void set_wrap_r(GLint value) 1332 | { glTexParameteri(target, GL_TEXTURE_WRAP_R, value); } 1333 | 1334 | void set_wrap(GLint value) 1335 | { 1336 | set_wrap_s(value); 1337 | set_wrap_t(value); 1338 | set_wrap_r(value); 1339 | } 1340 | 1341 | void set_border_color(float cr, float cg, float cb) 1342 | { 1343 | float color[] = {cr, cg, cb}; 1344 | glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color); 1345 | } 1346 | 1347 | void set_min_filter(GLint value) 1348 | { glTexParameteri(target, GL_TEXTURE_MIN_FILTER, value); } 1349 | 1350 | void set_mag_filter(GLint value) 1351 | { glTexParameteri(target, GL_TEXTURE_MAG_FILTER, value); } 1352 | 1353 | void set_filter(GLint value) 1354 | { 1355 | set_min_filter(value); 1356 | set_mag_filter(value); 1357 | } 1358 | 1359 | // void generate_mipmap() { 1360 | // glGenerateMipmap(target); 1361 | // } 1362 | 1363 | int get_param_i(GLenum pname) const 1364 | { 1365 | bind(); 1366 | GLint val; 1367 | glGetTexLevelParameteriv(target, 0, pname, &val); 1368 | return val; 1369 | } 1370 | 1371 | GLint internalformat() const 1372 | { return get_param_i(GL_TEXTURE_INTERNAL_FORMAT); } 1373 | 1374 | int width() const 1375 | { return get_param_i(GL_TEXTURE_WIDTH); } 1376 | 1377 | int height() const 1378 | { return get_param_i(GL_TEXTURE_HEIGHT); } 1379 | 1380 | int channels() const 1381 | { 1382 | switch(internalformat()) { 1383 | case GL_DEPTH_COMPONENT: 1384 | case GL_DEPTH_COMPONENT16: 1385 | case GL_DEPTH_COMPONENT24: 1386 | case GL_DEPTH_COMPONENT32F: 1387 | case GL_DEPTH_STENCIL: 1388 | case GL_DEPTH32F_STENCIL8: 1389 | case GL_DEPTH24_STENCIL8: 1390 | case GL_RED: 1391 | case GL_R8: 1392 | case GL_R32F: 1393 | return 1; 1394 | case GL_RG: 1395 | case GL_RG8: 1396 | case GL_RG32F: 1397 | return 2; 1398 | case GL_RGB: 1399 | case GL_RGB8: 1400 | case GL_RGB32F: 1401 | return 3; 1402 | case GL_RGBA: 1403 | case GL_RGBA8: 1404 | case GL_RGBA32F: 1405 | return 4; 1406 | default: 1407 | // ERROR unknown 1408 | return 0; 1409 | } 1410 | } 1411 | 1412 | GLenum format() const 1413 | { 1414 | switch(internalformat()) { 1415 | case GL_DEPTH_COMPONENT: 1416 | case GL_DEPTH_COMPONENT16: 1417 | case GL_DEPTH_COMPONENT24: 1418 | case GL_DEPTH_COMPONENT32F: 1419 | return GL_DEPTH_COMPONENT; 1420 | case GL_DEPTH_STENCIL: 1421 | case GL_DEPTH32F_STENCIL8: 1422 | case GL_DEPTH24_STENCIL8: 1423 | return GL_DEPTH_STENCIL; 1424 | case GL_RED: 1425 | case GL_R8: 1426 | case GL_R32F: 1427 | return GL_RED; 1428 | case GL_RG: 1429 | case GL_RG8: 1430 | case GL_RG32F: 1431 | return GL_RG; 1432 | case GL_RGB: 1433 | case GL_RGB8: 1434 | case GL_RGB32F: 1435 | return GL_RGB; 1436 | case GL_RGBA: 1437 | case GL_RGBA8: 1438 | case GL_RGBA32F: 1439 | return GL_RGBA; 1440 | default: 1441 | // ERROR unknown 1442 | return 0; 1443 | } 1444 | } 1445 | 1446 | static void unbind() 1447 | { glBindTexture(target, detail::INVALID_ID); } 1448 | 1449 | static void activate_unit(unsigned int num) 1450 | { glActiveTexture(GL_TEXTURE0 + num); } 1451 | }; 1452 | 1453 | struct texture_2d 1454 | : public texture_base 1455 | { 1456 | texture_2d() 1457 | {} 1458 | 1459 | texture_2d(glid_t tex_id) 1460 | : texture_base(tex_id) 1461 | {} 1462 | 1463 | void set_image_impl(GLint internalformat, unsigned w, unsigned h, GLenum format, GLenum type, const void* data) 1464 | { 1465 | bind(); 1466 | glTexImage2D(target, 1467 | 0, // level: use base image level 1468 | internalformat, 1469 | w, h, 1470 | 0, // must be 0 1471 | format, // format of source data 1472 | type, // type of source data 1473 | data); 1474 | } 1475 | 1476 | template 1477 | void set_image(GLint internalformat, unsigned w, unsigned h, const S* data=0) 1478 | { 1479 | // internalformat is i.e. GL_RGBA8, GL_RGB32F, ... 1480 | set_image_impl(internalformat, w, h, detail::texture_format::result, detail::texture_type::result, data); 1481 | } 1482 | 1483 | template 1484 | void set_image_depth(GLint internalformat, unsigned w, unsigned h, const S* data=0) 1485 | { 1486 | // internalformat is i.e. GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32F 1487 | set_image_impl(internalformat, w, h, GL_DEPTH_COMPONENT, detail::texture_type::result, data); 1488 | } 1489 | 1490 | void set_image_depth_stencil(GLint internalformat, unsigned w, unsigned h) 1491 | { 1492 | // internalformat is i.e. GL_DEPTH32F_STENCIL8, GL_DEPTH24_STENCIL8 1493 | GLenum type = 0; 1494 | if(internalformat == GL_DEPTH24_STENCIL8) type = GL_UNSIGNED_INT_24_8; 1495 | if(internalformat == GL_DEPTH32F_STENCIL8) type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; // ?? 1496 | set_image_impl(internalformat, w, h, GL_DEPTH_STENCIL, type, 0); 1497 | } 1498 | 1499 | template 1500 | std::vector get_image() const 1501 | { 1502 | bind(); 1503 | if(internalformat() == GL_DEPTH24_STENCIL8) { 1504 | std::vector buff(width()*height()); 1505 | glGetTexImage(target, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, buff.data()); 1506 | std::vector buff2(width()*height()); 1507 | for(size_t i=0; i> 24); // depth 1508 | for(size_t i=0; i buff(width()*height()*channels()); 1513 | glGetTexImage(target, 0, format(), detail::texture_type::result, buff.data()); 1514 | return buff; 1515 | } 1516 | } 1517 | 1518 | template 1519 | static texture_2d create_normal(GLint internalformat, unsigned w, unsigned h, const S* data=0) 1520 | { 1521 | texture_2d tex; 1522 | tex.create(); 1523 | tex.set_image(internalformat, w, h, data); 1524 | return tex; 1525 | } 1526 | 1527 | template 1528 | static texture_2d create_depth(GLint internalformat, unsigned w, unsigned h, const S* data=0) 1529 | { 1530 | texture_2d tex; 1531 | tex.create(); 1532 | tex.set_image_depth(internalformat, w, h, data); 1533 | return tex; 1534 | } 1535 | 1536 | }; 1537 | 1538 | struct texture_cube_map 1539 | : public texture_base 1540 | { 1541 | static GLenum cube_map_type(unsigned i) 1542 | { 1543 | constexpr GLenum types[6] = { 1544 | GL_TEXTURE_CUBE_MAP_NEGATIVE_X, // left 1545 | GL_TEXTURE_CUBE_MAP_POSITIVE_Z, // front 1546 | GL_TEXTURE_CUBE_MAP_POSITIVE_X, // right 1547 | GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, // back 1548 | GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, // bottom 1549 | GL_TEXTURE_CUBE_MAP_POSITIVE_Y // top 1550 | }; 1551 | return types[i]; 1552 | } 1553 | 1554 | texture_cube_map() 1555 | {} 1556 | 1557 | texture_cube_map(glid_t tex_id) 1558 | : texture_base(tex_id) 1559 | {} 1560 | 1561 | template 1562 | void set_image_mm(GLenum target, GLint internalformat, unsigned level, unsigned w, unsigned h, const S* data=0) 1563 | { 1564 | bind(); 1565 | glTexImage2D(target, 1566 | level, // level: use base image level 1567 | internalformat, // i.e. GL_RGBA8, GL_R32F, GL_RG16UI ... 1568 | w, h, 1569 | 0, // must be 0 1570 | detail::texture_format::result, // format of source data 1571 | detail::texture_type::result, // type of source data 1572 | data); 1573 | } 1574 | 1575 | template 1576 | void set_image(GLenum target, GLint internalformat, unsigned level, unsigned w, unsigned h, const S* data=0) 1577 | { 1578 | set_image_mm(target, internalformat, 0, w, h, data); 1579 | } 1580 | 1581 | template 1582 | void set_image(GLint internalformat, unsigned w, unsigned h, const std::vector& data={}) 1583 | { 1584 | bind(); 1585 | for(int i=0; i<6; i++) { 1586 | glTexImage2D(cube_map_type(i), 1587 | 0, // level: use base image level 1588 | internalformat, // i.e. GL_RGBA8, GL_R32F, GL_RG16UI ... 1589 | w, h, 1590 | 0, // must be 0 1591 | detail::texture_format::result, // format of source data 1592 | detail::texture_type::result, // type of source data 1593 | i < data.size() ? data[i] : 0); 1594 | } 1595 | } 1596 | 1597 | template 1598 | std::vector> get_image() const 1599 | { 1600 | std::vector> result(6, std::vector(width()*height()*channels())); 1601 | for(int i=0; i<6; i++) { 1602 | bind(); 1603 | glGetTexImage(cube_map_type(i), 1604 | 0, // level: use base image level 1605 | format(), 1606 | detail::texture_type::result, 1607 | result[i].data()); 1608 | } 1609 | return result; 1610 | } 1611 | 1612 | }; 1613 | 1614 | namespace TextureModes 1615 | { 1616 | enum def { 1617 | NORM=0, 1618 | SNORM=1, 1619 | FLOAT=2, 1620 | INT=3, 1621 | UINT=4 1622 | }; 1623 | } 1624 | typedef TextureModes::def TextureMode; 1625 | 1626 | namespace detail 1627 | { 1628 | template struct texture_internal_format; 1629 | #define PASTRY_DETAIL_TEX_CHANNELS_1 R 1630 | #define PASTRY_DETAIL_TEX_CHANNELS_2 RG 1631 | #define PASTRY_DETAIL_TEX_CHANNELS_3 RGB 1632 | #define PASTRY_DETAIL_TEX_CHANNELS_4 RGBA 1633 | #define PASTRY_DETAIL_TEX_CHANNELS(C) PASTRY_DETAIL_TEX_CHANNELS_##C 1634 | #define PASTRY_DETAIL_TEX_MODE_0 1635 | #define PASTRY_DETAIL_TEX_MODE_1 _SNORM 1636 | #define PASTRY_DETAIL_TEX_MODE_2 F 1637 | #define PASTRY_DETAIL_TEX_MODE_3 I 1638 | #define PASTRY_DETAIL_TEX_MODE_4 UI 1639 | #define PASTRY_DETAIL_TEX_MODE(M) PASTRY_DETAIL_TEX_MODE_##M 1640 | #define PASTRY_DETAIL_TEX_CONCAT4_(A, B, C, D) A ## B ## C ## D 1641 | #define PASTRY_DETAIL_TEX_CONCAT4(A, B, C, D) PASTRY_DETAIL_TEX_CONCAT4_(A, B, C, D) 1642 | #define PASTRY_DETAIL_TEX_FORMAT(C,M,BPC) \ 1643 | PASTRY_DETAIL_TEX_CONCAT4(GL_, PASTRY_DETAIL_TEX_CHANNELS(C), BPC, PASTRY_DETAIL_TEX_MODE(M)) 1644 | #define TEXTURE_INTERNAL_FORMAT(C,M,BPC) \ 1645 | template<> struct texture_internal_format { \ 1646 | static constexpr GLint result = PASTRY_DETAIL_TEX_FORMAT(C,M,BPC); \ 1647 | }; 1648 | #define PASTRY_DETAIL_TEX_DEF_N(C,M) \ 1649 | TEXTURE_INTERNAL_FORMAT(C, M, 8) \ 1650 | TEXTURE_INTERNAL_FORMAT(C, M, 16) 1651 | #define PASTRY_DETAIL_TEX_DEF_I(C,M) \ 1652 | TEXTURE_INTERNAL_FORMAT(C, M, 8) \ 1653 | TEXTURE_INTERNAL_FORMAT(C, M, 16) \ 1654 | TEXTURE_INTERNAL_FORMAT(C, M, 32) 1655 | #define PASTRY_DETAIL_TEX_DEF_F(C,M) \ 1656 | TEXTURE_INTERNAL_FORMAT(C, M, 16) \ 1657 | TEXTURE_INTERNAL_FORMAT(C, M, 32) 1658 | #define PASTRY_DETAIL_TEX_DEF(C) \ 1659 | PASTRY_DETAIL_TEX_DEF_N(C, 0) \ 1660 | PASTRY_DETAIL_TEX_DEF_N(C, 1) \ 1661 | PASTRY_DETAIL_TEX_DEF_F(C, 2) \ 1662 | PASTRY_DETAIL_TEX_DEF_I(C, 3) \ 1663 | PASTRY_DETAIL_TEX_DEF_I(C, 4) 1664 | PASTRY_DETAIL_TEX_DEF(1) 1665 | PASTRY_DETAIL_TEX_DEF(2) 1666 | PASTRY_DETAIL_TEX_DEF(3) 1667 | PASTRY_DETAIL_TEX_DEF(4) 1668 | #undef TEXTURE_INTERNAL_FORMAT 1669 | } 1670 | 1671 | template 1672 | struct texture 1673 | : public texture_2d 1674 | { 1675 | constexpr GLint internal_format() 1676 | { return detail::texture_internal_format::result; } 1677 | 1678 | template 1679 | void set_image(unsigned w, unsigned h, const S* data) 1680 | { 1681 | set_image( 1682 | internal_format(), 1683 | w, h, data); 1684 | } 1685 | }; 1686 | 1687 | struct renderbuffer 1688 | : public detail::resource 1689 | { 1690 | void bind() 1691 | { glBindRenderbuffer(GL_RENDERBUFFER, id()); } 1692 | 1693 | void storage(GLenum internalformat, unsigned width, unsigned height) 1694 | { glRenderbufferStorage(GL_RENDERBUFFER, internalformat, width, height); } 1695 | }; 1696 | 1697 | struct framebuffer 1698 | : public detail::resource 1699 | { 1700 | enum class target { READ, WRITE, BOTH }; 1701 | 1702 | static GLenum GetTarget(target t) 1703 | { 1704 | switch(t) { 1705 | case target::READ: return GL_READ_FRAMEBUFFER; 1706 | case target::WRITE: return GL_DRAW_FRAMEBUFFER; 1707 | default: case target::BOTH: return GL_FRAMEBUFFER; 1708 | } 1709 | } 1710 | 1711 | void bind(target t=target::BOTH) 1712 | { glBindFramebuffer(GetTarget(t), id()); } 1713 | 1714 | void attach(GLenum attachment, const texture_2d& tex) 1715 | { glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex.id(), 0); } 1716 | 1717 | void attach(GLenum attachment, const renderbuffer& rbo) 1718 | { glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, attachment, GL_RENDERBUFFER, rbo.id()); } 1719 | 1720 | static void unbind(target t=target::BOTH) 1721 | { glBindFramebuffer(GetTarget(t), detail::INVALID_ID); } 1722 | }; 1723 | 1724 | /** Enables/disables an OpenGL capability like GL_BLEND and automatically restores the state 1725 | * Usage example: 1726 | * { capability{{GL_BLEND, true}}; 1727 | * // ... code to execute with blending enabled 1728 | * } 1729 | */ 1730 | struct capability { 1731 | private: 1732 | struct capability_impl { 1733 | capability_impl(GLenum cap, bool set_to) { 1734 | cap_ = cap; 1735 | was_enabled_ = glIsEnabled(cap_); 1736 | set_to_ = set_to; 1737 | if(was_enabled_ != set_to_) { 1738 | if(set_to_) { 1739 | glEnable(cap_); 1740 | } 1741 | else { 1742 | glDisable(cap_); 1743 | } 1744 | } 1745 | } 1746 | 1747 | ~capability_impl() { 1748 | if(was_enabled_ != set_to_) { 1749 | if(was_enabled_) { 1750 | glEnable(cap_); 1751 | } 1752 | else { 1753 | glDisable(cap_); 1754 | } 1755 | } 1756 | } 1757 | 1758 | private: 1759 | GLenum cap_; 1760 | bool was_enabled_; 1761 | bool set_to_; 1762 | }; 1763 | public: 1764 | capability(GLenum cap, bool set_to) 1765 | : capabilities_{{cap, set_to}} {} 1766 | capability(std::initializer_list caps_list) 1767 | : capabilities_(caps_list) {} 1768 | private: 1769 | std::vector capabilities_; 1770 | }; 1771 | 1772 | template 1773 | void with_capabilities(const capability& caps, F f) { 1774 | f(); 1775 | } 1776 | 1777 | }} 1778 | #endif 1779 | --------------------------------------------------------------------------------