SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SLGLVertexBuffer.cpp
Go to the documentation of this file.
1 /**
2  * \file SLGLVertexBuffer.cpp
3  * \brief Wrapper around an OpenGL Vertex Array Objects
4  * \date January 2016
5  * \authors Marcus Hudritsch
6  * \copyright http://opensource.org/licenses/GPL-3.0
7  * \remarks Please use clangformat to format the code. See more code style on
8  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
9 */
10 
11 #include <SLGLState.h>
12 #include <SLGLVertexBuffer.h>
13 
14 //-----------------------------------------------------------------------------
17 //-----------------------------------------------------------------------------
18 //! Constructor initializing with default values
20 {
21  _id = 0;
22  _numVertices = 0;
23  _sizeBytes = 0;
24  _outputIsInterleaved = false;
25  _usage = BU_stream;
26 }
27 //-----------------------------------------------------------------------------
28 /*! Deletes the OpenGL objects for the vertex array and the vertex buffer.
29 The vector _attribs with the attribute information is not cleared.
30 */
32 {
33  if (_id)
34  {
35  glDeleteBuffers(1, &_id);
36  _id = 0;
39  }
40 }
41 //-----------------------------------------------------------------------------
43 {
44  deleteGL();
45  _attribs.clear();
46 }
47 //-----------------------------------------------------------------------------
49 {
50  for (SLuint i = 0; i < _attribs.size(); ++i)
51  if (_attribs[i].type == type)
52  return (SLint)i;
53  return -1;
54 }
55 //-----------------------------------------------------------------------------
56 /*! Updates the specified vertex attribute. This works only for sequential
57 attributes and not for interleaved attributes. This is used e.g. for meshes
58 with vertex skinning. See SLMesh::draw where we have joint attributes.
59 */
61  SLint elementSize,
62  void* dataPointer)
63 {
64  assert(dataPointer && "No data pointer passed");
65  assert(elementSize > 0 && elementSize < 5 && "Element size invalid");
66 
67  // Get attribute index and check element size
68  SLint index = attribIndex(type);
69  if (index == -1)
70  SL_EXIT_MSG("Attribute type does not exist in VBO.");
71  if (_attribs[(SLuint)index].elementSize != elementSize)
72  SL_EXIT_MSG("Attribute element size differs.");
74  SL_EXIT_MSG("Interleaved buffers can't be updated.");
75 
76  // Generate the vertex buffer object if there is none
77  if (index && !_id)
78  glGenBuffers(1, &_id);
79 
80  _attribs[(SLuint)index].dataPointer = dataPointer;
81 
82  ///////////////////////////////////////////////
83  // copy sub-data into existing buffer object //
84  ///////////////////////////////////////////////
85 
86  glBindBuffer(GL_ARRAY_BUFFER, _id);
87  glBufferSubData(GL_ARRAY_BUFFER,
88  _attribs[(SLuint)index].offsetBytes,
89  _attribs[(SLuint)index].bufferSizeBytes,
90  _attribs[(SLuint)index].dataPointer);
92 }
93 //-----------------------------------------------------------------------------
94 /*! Generates the OpenGL VBO for one or more vertex attributes.
95 If the input data is an interleaved array (all attribute data pointer where
96 identical) also the output buffer will be generated as an interleaved array.
97 Vertex arrays with attributes that are updated can not be interleaved.
98 Vertex attributes with separate arrays can generate an interleaved or a
99 sequential vertex buffer.\n\n
100 <PRE>
101 \n Sequential attribute layout:
102 \n | Positions | Normals | TexCoords |
103 \n Attribs: | Position0 | Position1 | Normal0 | Normal1 |TexCoord0|TexCoord1|
104 \n Elements: | PX | PY | PZ | PX | PY | PZ | NX | NY | NZ | NX | NY | NZ | TX | TY | TX | TY |
105 \n Bytes: |#### #### ####|#### #### ####|#### #### ####|#### #### ####|#### ####|#### ####|
106 \n | | |
107 \n |<------ offset Normals ----->| |
108 \n |<-------------------- offset TexCoords ------------------->|
109 \n
110 \n Interleaved attribute layout:
111 \n | Vertex 0 | Vertex 1 |
112 \n Attribs: | Position0 | Normal0 |TexCoord0| Position1 | Normal1 |TexCoord1|
113 \n Elements: | PX | PY | PZ | NX | NY | NZ | TX | TY | PX | PY | PZ | NX | NY | NZ | TX | TY |
114 \n Bytes: |#### #### ####|#### #### ####|#### ####|#### #### ####|#### #### ####|#### ####|
115 \n | | | |
116 \n |<-offsetN=32->| | |
117 \n |<------- offsetTC=32 ------->| |
118 \n | |
119 \n |<---------- strideBytes=32 ----------->|
120 </PRE>
121 */
123  SLGLBufferUsage usage,
124  SLbool outputInterleaved)
125 {
126  assert(numVertices);
127 
128  // if buffers exist delete them first
129  deleteGL();
130 
131  _numVertices = numVertices;
132  _usage = usage;
133  _outputIsInterleaved = outputInterleaved;
134 
135  // Generate the vertex buffer object
136  if (_attribs.size())
137  {
138  glGenBuffers(1, &_id);
139  glBindBuffer(GL_ARRAY_BUFFER, _id);
140  }
141 
142  // Check first if all attribute data pointer point to the same interleaved data
143  _inputIsInterleaved = false;
144  if (_attribs.size() > 1)
145  {
146  _inputIsInterleaved = true;
147  for (auto a : _attribs)
148  {
149  if (a.dataPointer != _attribs[0].dataPointer)
150  {
151  _inputIsInterleaved = false;
152  break;
153  }
154  }
155  }
156 
157  ///////////////////////////////////////////////////////
158  // Calculate total VBO size & attribute stride & offset
159  ///////////////////////////////////////////////////////
160 
161  _sizeBytes = 0;
162  _strideBytes = 0;
163 
165  {
166  _outputIsInterleaved = true;
167 
168  for (SLuint i = 0; i < _attribs.size(); ++i)
169  {
170  SLuint elementSizeBytes = (SLuint)_attribs[i].elementSize *
171  sizeOfType(_attribs[i].dataType);
172  _attribs[i].offsetBytes = _strideBytes;
173  _attribs[i].bufferSizeBytes = elementSizeBytes * _numVertices;
174  _sizeBytes += _attribs[i].bufferSizeBytes;
175  _strideBytes += elementSizeBytes;
176  }
177  }
178  else // input is in separate attribute data blocks
179  {
180  for (SLuint i = 0; i < _attribs.size(); ++i)
181  {
182  SLuint elementSizeBytes = (SLuint)_attribs[i].elementSize *
183  sizeOfType(_attribs[i].dataType);
185  _attribs[i].offsetBytes = _strideBytes;
186  else
187  _attribs[i].offsetBytes = _sizeBytes;
188  _attribs[i].bufferSizeBytes = elementSizeBytes * _numVertices;
189  _sizeBytes += _attribs[i].bufferSizeBytes;
190  if (_outputIsInterleaved) _strideBytes += elementSizeBytes;
191  }
192  }
193 
194  //////////////////////////////
195  // Generate VBO for Attributes
196  //////////////////////////////
197 
199  {
200  // generate the interleaved VBO buffer on the GPU
201  glBufferData(GL_ARRAY_BUFFER,
202  _sizeBytes,
203  _attribs[0].dataPointer,
204  _usage);
205  }
206  else // input is in separate attribute data block
207  {
208  if (_outputIsInterleaved) // Copy attribute data interleaved
209  {
210  SLVuchar data;
211  data.resize(_sizeBytes);
212  for (auto a : _attribs)
213  {
214  SLuint elementSizeBytes = (SLuint)a.elementSize *
215  sizeOfType(a.dataType);
216 
217  // Copy attributes interleaved
218  for (SLuint v = 0; v < _numVertices; ++v)
219  {
220  SLuint iDst = v * _strideBytes + a.offsetBytes;
221  SLuint iSrc = v * elementSizeBytes;
222  for (SLuint b = 0; b < elementSizeBytes; ++b)
223  data[iDst + b] = ((SLuchar*)a.dataPointer)[iSrc + b];
224  }
225 
226  // generate the interleaved VBO buffer on the GPU
227  glBufferData(GL_ARRAY_BUFFER,
228  _sizeBytes,
229  &data[0],
230  _usage);
231  }
232  }
233  else // copy attributes buffers sequentially
234  {
235  // allocate the VBO buffer on the GPU
236  glBufferData(GL_ARRAY_BUFFER,
237  _sizeBytes,
238  nullptr,
239  _usage);
240 
241  for (auto a : _attribs)
242  {
243  if (a.location > -1)
244  {
245  // Copies the attributes data at the right offset into the VBO
246  glBufferSubData(GL_ARRAY_BUFFER,
247  a.offsetBytes,
248  a.bufferSizeBytes,
249  a.dataPointer);
250  }
251  }
252  }
253  }
254 
257  GET_GL_ERROR;
258 }
259 
260 //-----------------------------------------------------------------------------
261 /*! This method is only used by SLGLVertexArray drawing methods for OpenGL
262  * contexts prior to 3.0 where vertex array objects did not exist. This is the
263  * additional overhead that had to be done per draw call.
264  * For VAO (Vertex Array Objects) this method is only used once at the creation
265  * of the VAO. The instanceDivisor number is only used for instanced drawing.
266  * For all other usage it is by default 0.
267  */
269 {
270  /////////////////////////////////////////
271  // Associate VBO to Attribute location //
272  /////////////////////////////////////////
273 
274  glBindBuffer(GL_ARRAY_BUFFER, _id);
275 
276  if (_outputIsInterleaved) // Copy attribute data interleaved
277  {
278  for (auto a : _attribs)
279  {
280  if (a.location > -1)
281  { // Sets the vertex attribute data pointer to its corresponding GLSL variable
282  if (a.dataType == BT_int || a.dataType == BT_uint)
283  {
284  glVertexAttribIPointer((SLuint)a.location,
285  a.elementSize,
286  a.dataType,
288  (void*)(size_t)a.offsetBytes);
289  }
290  else
291  {
292  glVertexAttribPointer((SLuint)a.location,
293  a.elementSize,
294  a.dataType,
295  GL_FALSE,
297  (void*)(size_t)a.offsetBytes);
298  }
299 
300  // Tell the attribute to be an array attribute instead of a state variable
301  glEnableVertexAttribArray((SLuint)a.location);
302 
303  // Special setting for instanced drawing
304  if (instanceDivisor > 0)
305  glVertexAttribDivisor((SLuint)a.location,
306  instanceDivisor);
307  }
308  }
309  }
310  else // copy attributes buffers sequentially
311  {
312  for (auto a : _attribs)
313  {
314  if (a.location > -1)
315  {
316  // Sets the vertex attribute data pointer to its corresponding GLSL variable
317  if (a.dataType == BT_int || a.dataType == BT_uint)
318  {
319  glVertexAttribIPointer((SLuint)a.location,
320  a.elementSize,
321  a.dataType,
322  0,
323  (void*)(size_t)a.offsetBytes);
324  }
325  else
326  {
327  glVertexAttribPointer((SLuint)a.location,
328  a.elementSize,
329  a.dataType,
330  GL_FALSE,
331  0,
332  (void*)(size_t)a.offsetBytes);
333  }
334 
335  // Tell the attribute to be an array attribute instead of a state variable
336  glEnableVertexAttribArray((SLuint)a.location);
337 
338  // Special setting for instanced drawing
339  if (instanceDivisor > 0)
340  glVertexAttribDivisor((SLuint)a.location,
341  instanceDivisor);
342  }
343  }
344  }
345 }
346 //-----------------------------------------------------------------------------
347 /*! This method is only used by SLGLVertexArray drawing methods for OpenGL
348 contexts prior to 3.0 where vertex array objects did not exist. This is the
349 additional overhead that had to be done per draw call.
350 */
352 {
353  if (_attribs.size())
354  {
355  for (auto a : _attribs)
356  if (a.location > -1)
357  glDisableVertexAttribArray((SLuint)a.location);
358  }
359 }
360 //-----------------------------------------------------------------------------
361 //! Returns the size in byte depending off the buffer type
363 {
364  switch (type)
365  {
366  case BT_float: return sizeof(SLfloat);
367  case BT_int: return sizeof(SLint);
368  case BT_ubyte: return sizeof(SLuchar);
369  case BT_ushort: return sizeof(SLushort);
370  case BT_uint: return sizeof(SLint);
371  default: SL_EXIT_MSG("Invalid buffer data type");
372  }
373  return 0;
374 }
375 //-----------------------------------------------------------------------------
float SLfloat
Definition: SL.h:173
unsigned int SLuint
Definition: SL.h:171
unsigned char SLuchar
Definition: SL.h:163
vector< SLuchar > SLVuchar
Definition: SL.h:193
bool SLbool
Definition: SL.h:175
unsigned short SLushort
Definition: SL.h:169
#define SL_EXIT_MSG(message)
Definition: SL.h:240
int SLint
Definition: SL.h:170
SLGLAttributeType
Enumeration for float vertex attribute types.
Definition: SLGLEnums.h:45
SLGLBufferUsage
Enumeration for buffer usage types also supported by OpenGL ES.
Definition: SLGLEnums.h:90
@ BU_stream
Buffer will be modified once and used at most a few times.
Definition: SLGLEnums.h:92
SLGLBufferType
Enumeration for buffer data types.
Definition: SLGLEnums.h:20
@ BT_ubyte
vertex index type (0-2^8)
Definition: SLGLEnums.h:23
@ BT_ushort
vertex index type (0-2^16)
Definition: SLGLEnums.h:24
@ BT_float
float vertex attributes
Definition: SLGLEnums.h:21
@ BT_int
int vertex attributes
Definition: SLGLEnums.h:22
@ BT_uint
vertex index type (0-2^32)
Definition: SLGLEnums.h:25
Singleton class for global render state.
#define GET_GL_ERROR
Definition: SLGLState.h:56
Wrapper class around OpenGL Vertex Buffer Objects (VBO)
SLint attribIndex(SLGLAttributeType type)
Returns the vector index if a vertex attribute exists otherwise -1.
void generate(SLuint numVertices, SLGLBufferUsage usage=BU_static, SLbool outputInterleaved=true)
Generates the VBO.
SLuint _sizeBytes
Distance for interleaved attributes in bytes.
static SLuint totalBufferSize
static total no. of buffers in use
SLbool _outputIsInterleaved
Vector of vertex attributes.
SLGLBufferUsage _usage
Total size of float VBO in bytes.
static SLuint totalBufferCount
SLVVertexAttrib _attribs
NO. of vertices in array.
SLuint _numVertices
OpenGL id of vertex buffer object.
SLGLVertexBuffer()
Constructor initializing with default values.
SLbool _inputIsInterleaved
Flag if VBO should be generated interleaved.
void clear()
Calls deleteGL & clears the attributes.
static SLuint sizeOfType(SLGLBufferType type)
static total size of all buffers in bytes
void deleteGL()
Deletes all vertex array & vertex buffer objects.
SLuint _strideBytes
Flag if VBO should be generated interleaved.
void disableAttrib()
disables the vertex attribute for OpenGL < 3.0
void bindAndEnableAttrib(SLuint divisor=0) const
Binds & enables the vertex attribute for OpenGL < 3.0 and during VAO creation.
void updateAttrib(SLGLAttributeType type, SLint elementSize, void *dataPointer)
Updates a specific vertex attribute in the VBO.