1 #pragma once
2 // -----------------------------------------------------------------------------
3 #include "GraphicsUtil.h"
4 #include "Vector.h"
5 #include <vector>
6 #include <tuple>
7 #include <map>
8 #include <array>
9 #include <type_traits>
10 #include <cstdlib>
11 #include <string.h>
12 #include <glm/vec2.hpp>
13 // -----------------------------------------------------------------------------
14 // DESCRIPTORS
15 // -----------------------------------------------------------------------------
16 // Describes a single array held in the vbo
17 struct BufferDesc {
BufferDescBufferDesc18   BufferDesc(const char * _attr_name, GLenum _type_size, size_t _type_dim,
19              size_t _data_size, const void * _data_ptr, bool _data_norm)
20     : attr_name(_attr_name), type_size(_type_size), type_dim(_type_dim),
21       data_size(_data_size), data_ptr(_data_ptr), data_norm(_data_norm)
22       {}
23 
24   // Constructor for just layout
BufferDescBufferDesc25   BufferDesc(const char * _attr_name, GLenum _type_size, size_t _type_dim,
26              size_t _offset, bool _data_norm)
27     : attr_name(_attr_name), type_size(_type_size), type_dim(_type_dim),
28       data_norm(_data_norm), offset(_offset) {}
29 
30   // Constructor used for index buffers
31   BufferDesc(GLenum _type_size, size_t _data_size, const void * _data_ptr, size_t _offset = 0)
type_sizeBufferDesc32     : type_size(_type_size), data_size(_data_size), data_ptr(_data_ptr),
33       offset(_offset) {}
34 
35   // Constructor used for data replication
BufferDescBufferDesc36   BufferDesc(const char * _attr_name, GLuint _gl_id) :
37     attr_name(_attr_name), gl_id(_gl_id) {}
38 
39   const char * attr_name { nullptr };
40   GLenum type_size { GL_FLOAT };
41   size_t type_dim  { 0 };
42   size_t data_size { 0 };
43   const void * data_ptr  { nullptr };
44   bool   data_norm { false };
45   GLuint gl_id     { 0 };
46   size_t offset    { 0 };
47 };
48 using BufferDataDesc = std::vector< BufferDesc >;
49 
50 /* different types of AttribOp */
51 enum attrib_op_type {
52   NO_COPY = 0,
53   FLOAT_TO_FLOAT,
54   FLOAT2_TO_FLOAT2,
55   FLOAT3_TO_FLOAT3,
56   FLOAT4_TO_FLOAT4,
57   FLOAT3_TO_UB3,
58   FLOAT1_TO_UB_4TH,
59   UB3_TO_UB3,
60   UINT_INT_TO_PICK_DATA,
61   UB1_INTERP_TO_CAP,
62   FLOAT1_TO_INTERP,
63   UB4_TO_UB4,
64   PICK_DATA_TO_PICK_DATA,
65   CYL_CAP_TO_CAP,
66   FLOAT1_INTERP_TO_CAP,
67   UB1_TO_INTERP,
68   CYL_CAPS_ARE_ROUND,
69   CYL_CAPS_ARE_FLAT,
70   CYL_CAPS_ARE_CUSTOM,
71   FLOAT4_TO_UB4
72 };
73 
74 struct AttribDesc;
75 
76 typedef void (*AttribOpFuncDataFunctionPtr)(void *varData, const float * pc, void *globalData, int idx);
77 
78 /* AttribOpFuncData : This structure holds information a callback that sets/post-processes
79                       data for a particular attribute.  Currently, a list of these functions
80                       are attached to the AttribOp so that when vertices are created (i.e., incr_vertices > 0)
81                       then for each vertex, this function is called for the particular attribute attribName.
82 
83    funcDataConversion - pointer to function that sets/post-processes the attribute data
84    funcDataGlobalArg - pointer to global structure that can be used in each call to the callback
85    attribName - attribute name this function is processing. (this calling function specifies the name, and the
86                 CGOConvertToShader() sets attrib to the associated AttribDesc.
87  */
88 struct AttribOpFuncData {
89   void (*funcDataConversion)(void *varData, const float * pc, void *globalData, int idx); // if set, should be called on every output value for this attribute
90   void *funcDataGlobalArg;
91   const char *attribName;
92   AttribDesc *attrib;
93   AttribOpFuncDataFunctionPtr _funcDataConversion;
AttribOpFuncDataAttribOpFuncData94   AttribOpFuncData(AttribOpFuncDataFunctionPtr _funcDataConversion,
95                    void *_funcDataGlobalArg,
96                    const char *_attribName)
97   : funcDataConversion(_funcDataConversion), funcDataGlobalArg(_funcDataGlobalArg), attribName(_attribName), attrib(NULL){}
98 };
99 
100 using AttribOpFuncDataDesc = std::vector< AttribOpFuncData >;
101 
102 /*
103  * defines an operation that copies and (optionally) creates new vertices in
104  * a VBO operation for a particular CGO operation (op).
105  *
106  * op - the CGO operation
107  * order - the order for this operation to be executed for the given CGO operation
108  * offset - the offset into the CGO operation to copy
109  * conv_type - type of copy (can be general or specific, see above, e.g. FLOAT3_TO_FLOAT3, UB1_TO_INTERP)
110  * incr_vertices - the number of vertices (if any) that are generated for the VBO after this operation
111  *                 is executed.
112  *
113  */
114 struct AttribOp {
115   AttribOp(unsigned short _op, size_t _order, size_t _conv_type, size_t _offset, size_t _incr_vertices=0, int _copyFromAttr=-1)
opAttribOp116     : op(_op)
117     , order(_order)
118     , offset(_offset)
119     , conv_type(_conv_type)
120     , incr_vertices(_incr_vertices)
121     , copyFromAttr(_copyFromAttr)
122     {}
123   unsigned short op { 0 };
124   size_t order { 0 };
125   size_t offset  { 0 };
126   size_t conv_type { 0 };
127   size_t incr_vertices { 0 };
128   int copyFromAttr { -1 };
129   struct AttribDesc *desc { 0 };
130   struct AttribDesc *copyAttribDesc { 0 };
131   std::vector<AttribOpFuncData> funcDataConversions;
132 };
133 using AttribDataOp = std::vector< AttribOp >;
134 
135 /*
136  * defines an attribute that is used in a shader.  this description has all of the necessary information
137  * for our "optimize" function to generate either an array for input into the VBO or a call to the
138  * related glVertexAttrib() call when this attribute has the same value throughout the CGO.
139  *
140  * attr_name - the name of this attribute inside the shaders
141  * order - order of attribute used in VBO
142  * attrOps - all AttribOp for this particular attribute.  This allows the user to define how this
143  *           attribute gets populated from the primitive CGO's one or many CGO operations.
144  * default_value - pointer to the default value of this attribute (optional, needs to be the same
145  *                 size of the attribute's type)
146  * repeat_value/repeat_value_length - specified if the attribute has repeating values
147  *                                    repeat_value - a pointer to the type and data for repeat values
148  *                                    repeat_value_length - number of repeat values
149  * type_size - size of type for this attribute (e.g., GL_FLOAT, GL_UNSIGNED_BYTE)
150  * type_dim - number of primitives (i.e., type_size) for each vertex of this attribute
151  * data_norm - whether this attribute is normalized when passed to the VBO (GL_TRUE or GL_FALSE)
152  *
153  */
154 struct AttribDesc {
155   AttribDesc(const char * _attr_name, GLenum _type_size, size_t _type_dim, bool _data_norm, AttribDataOp _attrOps={})
attr_nameAttribDesc156     : attr_name(_attr_name)
157     , attrOps(_attrOps)
158     , default_value(NULL)
159     , type_size(_type_size)
160     , type_dim(_type_dim)
161     , data_norm(_data_norm)
162     {}
163   const char * attr_name { nullptr };
164   int order { 0 };
165   AttribDataOp attrOps { };
166   unsigned char *default_value { nullptr };
167   unsigned char *repeat_value { nullptr };
168   int repeat_value_length { 0 };
169   GLenum type_size { GL_FLOAT };
170   size_t type_dim  { 0 };
171   bool   data_norm { false };
172 };
173 using AttribDataDesc = std::vector< AttribDesc >;
174 
175 class gpuBuffer_t {
176   friend class CShaderMgr;
177 public:
~gpuBuffer_t()178   virtual ~gpuBuffer_t() {};
get_hash_id()179   virtual size_t get_hash_id() { return _hashid; }
180   virtual void bind() const = 0;
181 protected:
set_hash_id(size_t id)182   virtual void set_hash_id(size_t id) { _hashid = id; }
183 private:
184   size_t _hashid { 0 };
185 };
186 
187 // -----------------------------------------------------------------------------
188 /* Vertexbuffer rules:
189  * -----------------------------------------------------------------------------
190  * - If the buffer data is interleaved then buffer sub data functionality cannot
191  *   be used.
192  * - The same order of buffer data must be maintained when uploading and binding
193  *
194  *-----------------------------------------------------------------------
195  * USAGE_PATTERN:
196  * SEPARATE:
197  *   vbo1 [ data1 ]
198  *   vbo2 [ data2 ]
199  *   ...
200  *   vboN [ dataN ]
201  * SEQUENTIAL:
202  *   vbo [ data1 | data2 | ... | dataN ]
203  * INTERLEAVED:
204  *   vbo [ data1[0], data2[0], ..., dataN[0] | ... | data1[M], data2[M], ..., dataN[M] ]
205  */
206 template <GLenum _TYPE>
207 class GenericBuffer : public gpuBuffer_t {
208   friend class CShaderMgr;
209 public:
210   static const GLenum TYPE = _TYPE;
211 
212   enum buffer_layout {
213     SEPARATE,   // multiple vbos
214     SEQUENTIAL, // single vbo
215     INTERLEAVED // single vbo
216   };
217 
218   GenericBuffer( buffer_layout layout = SEPARATE, GLenum usage = GL_STATIC_DRAW ) :
m_buffer_usage(usage)219     m_buffer_usage(usage), m_layout(layout) {}
220 
~GenericBuffer()221   ~GenericBuffer() {
222     for (auto &d : m_desc) {
223       if (d.gl_id) {
224 	glDeleteBuffers(1, &d.gl_id);
225       }
226     }
227     if (m_interleavedID) {
228       glDeleteBuffers(1, &m_interleavedID);
229     }
230   }
231 
232   /***********************************************************************
233    * bufferData
234    *----------------------------------------------------------------------
235    * Takes a vector of the struct at the top of this file which describes
236    * the layout of the vbo object. The supplied data ptr in the struct can
237    * be zero, in which case if the default usage is STATIC_DRAW then no
238    * opengl buffer will be generated for that, else it is assumed that the
239    * data will be supplied at a later point because it's dynamic draw.
240    ***********************************************************************/
bufferData(BufferDataDesc && desc)241   bool bufferData(BufferDataDesc && desc) {
242     m_desc = std::move(desc);
243     return evaluate();
244   }
245 
bufferData(BufferDataDesc && desc,const void * data,size_t len,size_t stride)246   bool bufferData(BufferDataDesc && desc, const void * data, size_t len, size_t stride) {
247     bool ok = true;
248     m_desc = std::move(desc);
249     m_interleaved = true;
250     m_stride = stride;
251     ok = genBuffer(m_interleavedID, len, data);
252     return ok;
253   }
254 
255   // -----------------------------------------------------------------------------
256   // bufferSubData :
257   // This function assumes that the data layout hasn't change
258   bool bufferSubData(const void * data, size_t index = 0) {
259     auto &d = m_desc[index];
260     if (m_interleavedID) {
261       glBindBuffer(TYPE, m_interleavedID);
262     } else {
263       glBindBuffer(TYPE, d.gl_id);
264     }
265     glBufferSubData(TYPE, 0, d.data_size, data);
266     return glCheckOkay();
267   }
268 
269   void bufferSubData(size_t offset, size_t size, void * data, size_t index = 0) {
270     // maybe assert that the index is within range
271     if (m_interleavedID) {
272       glBindBuffer(TYPE, m_interleavedID);
273     } else {
274       glBindBuffer(TYPE, m_desc[index].gl_id);
275     }
276     glBufferSubData(TYPE, offset, size, data);
277   }
278 
279   // for interleaved dat only, replaces the whole interleaved vbo
bufferReplaceData(size_t offset,size_t len,const void * data)280   void bufferReplaceData(size_t offset, size_t len, const void * data) {
281     glBindBuffer(TYPE, m_interleavedID);
282     glBufferSubData(TYPE, offset, len, data);
283   }
284 
285 
286 protected:
287 
evaluate()288   bool evaluate() {
289     bool ok = true;
290     if (TYPE == GL_ELEMENT_ARRAY_BUFFER) {
291       ok = seqBufferData();
292     } else {
293       switch (m_layout) {
294       case SEPARATE:
295         ok = sepBufferData();
296         break;
297       case SEQUENTIAL:
298         ok = seqBufferData();
299         break;
300       case INTERLEAVED:
301         ok = interleaveBufferData();
302         break;
303       }
304     }
305     return ok;
306   }
307 
308   // USAGE PATTERNS
sepBufferData()309   bool sepBufferData() {
310     for ( auto &d : m_desc ) {
311       // If the specified size is 0 but we have a valid pointer
312       // then we are going to glVertexAttribXfv X in {1,2,3,4}
313       if (d.data_ptr && (m_buffer_usage == GL_STATIC_DRAW)) {
314 	if (d.data_size) {
315 	  if (!genBuffer(d.gl_id, d.data_size, d.data_ptr))
316 	    return false;
317 	}
318       }
319     }
320     return true;
321   }
322 
seqBufferData()323   bool seqBufferData() {
324     // this is only going to use a single opengl vbo
325     m_interleaved = true;
326 
327     size_t buffer_size { 0 };
328     for ( auto & d : m_desc ) {
329       buffer_size += d.data_size;
330     }
331 
332     uint8_t * buffer_data = new uint8_t[buffer_size];
333     uint8_t * data_ptr = buffer_data;
334     size_t offset = 0;
335 
336     for ( auto & d : m_desc ) {
337       d.offset = offset;
338       if (d.data_ptr)
339         memcpy(data_ptr, d.data_ptr, d.data_size);
340       else
341         memset(data_ptr, 0, d.data_size);
342       data_ptr += d.data_size;
343       offset += d.data_size;
344     }
345 
346     bool ok = true;
347     ok = genBuffer(m_interleavedID, buffer_size, buffer_data);
348     delete[] buffer_data;
349     return ok;
350   }
351 
interleaveBufferData()352   bool interleaveBufferData() {
353     size_t interleaved_size = 0;
354     const size_t buffer_count = m_desc.size();
355     size_t stride = 0;
356     std::vector<uint8_t *> data_table(buffer_count);
357     std::vector<uint8_t *> ptr_table(buffer_count);
358     std::vector<size_t> size_table(buffer_count);
359     size_t count = m_desc[0].data_size / (gl_sizeof(m_desc[0].type_size) * m_desc[0].type_dim);
360 
361     // Maybe assert that all pointers in d_desc are valid?
362     for ( size_t i = 0; i < buffer_count; ++i ) {
363       auto &d = m_desc[i];
364       size_t size = gl_sizeof(d.type_size);
365 
366       // offset is the current stride
367       d.offset = stride;
368 
369       // These must come after so that offset starts at 0
370       // Size of 3 normals or whatever the current type is
371       size_table[i] = size * d.type_dim;
372 
373       // Increase our current estimate of the stride by this amount
374       stride += size_table[i];
375 
376       // Does the addition of that previous stride leave us on a word boundry?
377       int m = stride % 4;
378       stride = (m ? (stride + (4 - m)) : stride);
379 
380       // data_table a pointer to the begining of each array
381       data_table[i] = (uint8_t *)d.data_ptr;
382 
383       // We will move these pointers along by the values in the size table
384       ptr_table[i] = data_table[i];
385 
386     }
387 
388     m_stride = stride;
389 
390     interleaved_size = count * stride;
391 
392     uint8_t *interleaved_data = (uint8_t *)calloc(interleaved_size, sizeof(uint8_t));
393     uint8_t *i_ptr = interleaved_data;
394 
395     while (i_ptr != (interleaved_data + interleaved_size)) {
396       for ( size_t i = 0; i < buffer_count; ++i ) {
397         if (ptr_table[i]){
398 	memcpy( i_ptr, ptr_table[i], size_table[i] );
399 	ptr_table[i] += size_table[i];
400         }
401 	i_ptr += size_table[i];
402       }
403     }
404 
405     bool ok = true;
406     ok = genBuffer(m_interleavedID, interleaved_size, interleaved_data);
407     m_interleaved = true;
408     free(interleaved_data);
409     return ok;
410   }
411 
genBuffer(GLuint & id,size_t size,const void * ptr)412   bool genBuffer(GLuint &id, size_t size, const void * ptr) {
413     glGenBuffers(1, &id);
414     if (!glCheckOkay())
415       return false;
416     glBindBuffer(TYPE, id);
417     if (!glCheckOkay())
418       return false;
419     glBufferData(TYPE, size, ptr, GL_STATIC_DRAW);
420     if (!glCheckOkay())
421       return false;
422     return true;
423   }
424 
425 protected:
426   bool m_status                { false };
427   bool m_interleaved           { false };
428   GLuint m_interleavedID       { 0 };
429   const GLenum m_buffer_usage  { GL_STATIC_DRAW };
430   const buffer_layout m_layout { SEPARATE };
431   size_t m_stride              { 0 };
432   BufferDataDesc     m_desc;
433 };
434 
435 /*
436  * Vertex buffer specialization
437  */
438 class VertexBuffer : public GenericBuffer<GL_ARRAY_BUFFER> {
bind_attrib(GLuint prg,const BufferDesc & d)439   void bind_attrib(GLuint prg, const BufferDesc & d) {
440     GLint loc = glGetAttribLocation(prg, d.attr_name);
441     bool masked = false;
442     for (GLint lid : m_attribmask)
443       if (lid == loc)
444         masked = true;
445     if ( loc >= 0 )
446       m_locs.push_back(loc);
447     if ( loc >= 0 && !masked ) {
448       if (!m_interleaved && d.gl_id)
449         glBindBuffer( TYPE, d.gl_id );
450       glEnableVertexAttribArray( loc );
451       glVertexAttribPointer( loc, d.type_dim, d.type_size, d.data_norm, m_stride, (const void *)d.offset );
452     }
453   };
454 
455 public:
456   VertexBuffer( buffer_layout layout = SEPARATE, GLenum usage = GL_STATIC_DRAW ) : GenericBuffer<GL_ARRAY_BUFFER>(layout, usage){}
457 
bind()458   void bind() const {
459     // we shouldn't use this one
460     if (m_interleaved)
461       glBindBuffer(TYPE, m_interleavedID);
462   }
463 
464   void bind(GLuint prg, int index = -1) {
465     if (index >= 0) {
466       glBindBuffer( TYPE, m_interleavedID );
467       bind_attrib(prg, m_desc[index]);
468     } else {
469       if (m_interleaved && m_interleavedID)
470         glBindBuffer( TYPE, m_interleavedID );
471       for (auto & d : m_desc) {
472         bind_attrib(prg, d);
473       }
474       m_attribmask.clear();
475     }
476   }
477 
unbind()478   void unbind() {
479     for (auto &d : m_locs) {
480       glDisableVertexAttribArray(d);
481     }
482     m_locs.clear();
483     glBindBuffer(TYPE, 0);
484   }
485 
maskAttributes(std::vector<GLint> attrib_locs)486   void maskAttributes(std::vector<GLint> attrib_locs) {
487     m_attribmask = std::move(attrib_locs);
488   }
489 
maskAttribute(GLint attrib_loc)490   void maskAttribute(GLint attrib_loc) {
491     m_attribmask.push_back(attrib_loc);
492   }
493 
replicate_data(const char * attrib_name,int index)494   void replicate_data(const char * attrib_name, int index) {
495     auto & d = m_desc[index];
496     BufferDesc newdesc(attrib_name, d.gl_id);
497     newdesc.offset    = d.offset;
498     newdesc.data_norm = d.data_norm;
499     newdesc.type_size = d.type_size;
500     newdesc.type_dim  = d.type_dim;
501     m_desc.push_back(newdesc);
502   }
503 
504 private:
505   // m_locs is only for interleaved data
506   std::vector<GLint> m_locs;
507   std::vector<GLint> m_attribmask;
508 };
509 
510 /*
511  * Index buffer specialization
512  */
513 class IndexBuffer : public GenericBuffer<GL_ELEMENT_ARRAY_BUFFER> {
514 public:
515   using GenericBuffer::GenericBuffer;
516 
bind()517   void bind() const {
518     glBindBuffer(TYPE, m_interleavedID);
519   }
520 
unbind()521   void unbind() {
522     glBindBuffer(TYPE, 0);
523   }
524 };
525 
526 // Forward Decls
527 class frameBuffer_t;
528 class renderBuffer_t;
529 
530 /***********************************************************************
531  * RENDERBUFFER
532  ***********************************************************************/
533 namespace rbo {
534   enum storage {
535     DEPTH16 = 0,
536     DEPTH24,
537     COUNT
538   };
539 
540   void unbind();
541 };
542 
543 class renderBuffer_t : public gpuBuffer_t {
544   friend class frameBuffer_t;
545   friend class CShaderMgr;
546 public:
renderBuffer_t(int width,int height,rbo::storage storage)547   renderBuffer_t(int width, int height, rbo::storage storage) :
548     _width(width), _height(height), _storage(storage) {
549     genBuffer();
550   }
~renderBuffer_t()551   ~renderBuffer_t() {
552     freeBuffer();
553   }
554 
555   void bind() const;
556   void unbind() const;
557 
558 private:
559   void genBuffer();
560   void freeBuffer();
561 
562 protected:
563   uint32_t     _id;
564   int          _dim[2];
565   int          _width;
566   int          _height;
567   rbo::storage _storage;
568 };
569 
570 /***********************************************************************
571  * TEXTURE
572  ***********************************************************************/
573 namespace tex {
574   enum class dim : int {
575     D1 = 0,
576     D2,
577     D3,
578     COUNT
579   };
580   enum class format : int {
581     R = (int)dim::COUNT,
582     RG,
583     RGB,
584     RGBA,
585     COUNT
586   };
587   enum class data_type : int {
588     UBYTE = (int)format::COUNT,
589     FLOAT,
590     HALF_FLOAT,
591     COUNT
592   };
593   enum class filter : int {
594     NEAREST = (int)data_type::COUNT,
595     LINEAR,
596     NEAREST_MIP_NEAREST,
597     NEAREST_MIP_LINEAR,
598     LINEAR_MIP_NEAREST,
599     LINEAR_MIP_LINEAR,
600     COUNT
601   };
602   enum class wrap : int {
603     REPEAT = (int)filter::COUNT,
604     CLAMP,
605     MIRROR_REPEAT,
606     CLAMP_TO_EDGE,
607     CLAMP_TO_BORDER,
608     MIRROR_CLAMP_TO_EDGE,
609     COUNT
610   };
611   enum class env_name : int {
612     ENV_MODE = (int)wrap::COUNT,
613     COUNT
614   };
615   enum class env_param : int {
616     REPLACE = (int)env_name::COUNT,
617     COUNT
618   };
619   const uint32_t max_params = (int)env_param::COUNT;
620 
621   void env(tex::env_name, tex::env_param);
622 };
623 
624 class textureBuffer_t : public gpuBuffer_t {
625   friend class frameBuffer_t;
626 public:
627   // Generates a 1D texture
textureBuffer_t(tex::format format,tex::data_type type,tex::filter mag,tex::filter min,tex::wrap wrap_s)628   textureBuffer_t(tex::format format, tex::data_type type,
629                   tex::filter mag, tex::filter min,
630                   tex::wrap wrap_s) :
631     _dim(tex::dim::D1), _format(format), _type(type),
632     _sampling({(int)mag, (int)min, (int)wrap_s, 0, 0})
633     {
634       genBuffer();
635     };
636   // Generates a 2D texture
textureBuffer_t(tex::format format,tex::data_type type,tex::filter mag,tex::filter min,tex::wrap wrap_s,tex::wrap wrap_t)637   textureBuffer_t(tex::format format, tex::data_type type,
638                   tex::filter mag, tex::filter min,
639                   tex::wrap wrap_s, tex::wrap wrap_t) :
640     _dim(tex::dim::D2), _format(format), _type(type),
641     _sampling({(int)mag, (int)min, (int)wrap_s, (int)wrap_t, 0})
642     {
643       genBuffer();
644     };
645   // Generates a 3D texture
textureBuffer_t(tex::format format,tex::data_type type,tex::filter mag,tex::filter min,tex::wrap wrap_s,tex::wrap wrap_t,tex::wrap wrap_r)646   textureBuffer_t(tex::format format, tex::data_type type,
647                   tex::filter mag, tex::filter min,
648                   tex::wrap wrap_s, tex::wrap wrap_t,
649                   tex::wrap wrap_r) :
650     _dim(tex::dim::D3), _format(format), _type(type),
651     _sampling({(int)mag, (int)min, (int)wrap_s, (int)wrap_t, (int)wrap_r})
652     {
653       genBuffer();
654     };
~textureBuffer_t()655   ~textureBuffer_t() {
656     freeBuffer();
657   }
658 
659   void bind() const;
660   void unbind() const;
661 
662   void texture_data_1D(int width, const void * data);
663   void texture_data_2D(int width, int height, const void * data);
664   void texture_data_3D(int width, int height, int depth, const void * data);
665 private:
666   void genBuffer();
667   void freeBuffer();
668 
669 private:
670   const tex::dim           _dim;
671   const tex::format        _format;
672   const tex::data_type     _type;
673   const std::array<int, 5> _sampling;
674   uint32_t                 _id     { 0 };
675   int                      _width  { 0 };
676   int                      _height { 0 };
677   int                      _depth  { 0 };
678 };
679 
680 /***********************************************************************
681  * FRAMEBUFFER
682  ***********************************************************************/
683 namespace fbo {
684   enum attachment {
685     COLOR0 = 0,
686     COLOR1,
687     COLOR2,
688     COLOR3,
689     DEPTH,
690     COUNT
691   };
692 
693   // global unbind for fbos
694   void unbind();
695 }
696 
697 class frameBuffer_t : public gpuBuffer_t {
698   friend class CShaderMgr;
699 public:
frameBuffer_t()700   frameBuffer_t() {
701     genBuffer();
702   }
~frameBuffer_t()703   ~frameBuffer_t() {
704     freeBuffer();
705   }
706 
707   void attach_texture(textureBuffer_t * texture, fbo::attachment loc);
708   void attach_renderbuffer(renderBuffer_t * renderbuffer, fbo::attachment loc);
709   void print_fbo();
710 
711   void bind() const;
712   void unbind() const;
713 private:
714   void genBuffer();
715   void freeBuffer();
716   void checkStatus();
717 
718 protected:
719   uint32_t _id { 0 };
720   std::vector<std::tuple<size_t, fbo::attachment>> _attachments;
721 };
722 
723 
724 /***********************************************************************
725  * RENDERTARGET
726  *----------------------------------------------------------------------
727  * A 2D render target that automatically has depth, used for postprocess
728  ***********************************************************************/
729 struct rt_layout_t {
730   enum data_type   { UBYTE, FLOAT };
rt_layout_trt_layout_t731   rt_layout_t(uint8_t _nchannels, data_type _type)
732     : nchannels(_nchannels), type(_type) {}
rt_layout_trt_layout_t733   rt_layout_t(uint8_t _nchannels, data_type _type, int _width, int _height)
734     : nchannels(_nchannels), type(_type), width(_width), height(_height) {}
735   uint8_t     nchannels;
736   data_type   type;
737   int         width  { 0 };
738   int         height { 0 };
739 };
740 
741 class renderTarget_t : public gpuBuffer_t {
742   friend class CShaderMgr;
743 public:
744   using shape_type = glm::ivec2;
745 
renderTarget_t(shape_type size)746   renderTarget_t(shape_type size) : _size(size) {}
renderTarget_t(int width,int height)747   renderTarget_t(int width, int height) : _size(width, height) {}
748   ~renderTarget_t();
749 
bind()750   void bind() const { bind(true); };
751   void bind(bool clear) const;
bindFBORBO()752   void bindFBORBO() const
753   {
754     _fbo->bind();
755     _rbo->bind();
756   }
757 
758   void layout(std::vector<rt_layout_t>&& desc, renderBuffer_t * with_rbo = nullptr);
759   void resize(shape_type size);
760 
size()761   const shape_type& size() const { return _size; };
762 
rbo()763   renderBuffer_t* rbo() const noexcept { return _rbo; }
fbo()764   frameBuffer_t* fbo() const noexcept { return _fbo; }
textures()765   const std::vector<textureBuffer_t*>& textures() const noexcept { return _textures; }
766 
767 protected:
768   bool _shared_rbo { false };
769   shape_type _size;
770   frameBuffer_t * _fbo;
771   renderBuffer_t * _rbo;
772   std::vector<rt_layout_t> _desc;
773   std::vector<textureBuffer_t *> _textures;
774 };
775