Developer Documentation
VertexDeclaration.cc
1 /*===========================================================================*\
2  * *
3  * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39  * *
40 \*===========================================================================*/
41 
42 
43 
44 #include <ACG/GL/acg_glew.hh>
45 #include <ACG/GL/gl.hh>
46 #include <ACG/GL/GLState.hh>
47 #include <ACG/ShaderUtils/GLSLShader.hh>
48 
49 #include <QTextStream>
50 
51 #include "VertexDeclaration.hh"
52 
53 
54 namespace ACG
55 {
56 
57 VertexElement::VertexElement()
58  : type_(0), numElements_(0), usage_(VERTEX_USAGE_SHADER_INPUT), shaderInputName_(0), pointer_(0), divisor_(0), vbo_(0)
59 {
60 }
61 
62 
63 void VertexElement::setByteOffset(unsigned int _offset)
64 {
65  // union cast instead of reinterpret_cast for cross-platform compatibility
66  union ptr2uint
67  {
68  unsigned long u;
69  const void* p;
70  } offset;
71 
72  offset.u = static_cast<unsigned long>(_offset);
73 
74  pointer_ = offset.p;
75 }
76 
77 unsigned int VertexElement::getByteOffset() const
78 {
79  // union cast instead of reinterpret_cast for cross-platform compatibility
80  union ptr2uint
81  {
82  unsigned long u;
83  const void* p;
84  } offset;
85 
86  offset.p = pointer_;
87 
88  return static_cast<unsigned int>(offset.u);
89 }
90 
91 bool VertexElement::operator ==(const VertexElement &_other) const
92 {
93  return (type_ == _other.type_) &&
94  (numElements_ == _other.numElements_) &&
95  (usage_ == _other.usage_) &&
96  (shaderInputName_ == _other.shaderInputName_) &&
97  (pointer_ == _other.pointer_) &&
98  (divisor_ == _other.divisor_) &&
99  (vbo_ == _other.vbo_);
100 }
101 
102 VertexDeclaration::VertexDeclaration()
103 : vertexStride_(0), strideUserDefined_(0)
104 {
105 
106 }
107 
108 
109 VertexDeclaration::~VertexDeclaration()
110 {
111 
112 }
113 
114 
116 {
117  addElements(1, _pElement);
118 }
119 
120 void VertexDeclaration::addElement(unsigned int _type,
121  unsigned int _numElements,
122  VERTEX_USAGE _usage,
123  const void* _pointer,
124  const char* _shaderInputName,
125  unsigned int _divisor,
126  unsigned int _vbo)
127 {
128  VertexElement* ve = new VertexElement();
129 
130  ve->type_ = _type;
131  ve->numElements_ = _numElements;
132  ve->usage_ = _usage;
133  ve->shaderInputName_ = _shaderInputName;
134  ve->pointer_ = _pointer;
135  ve->divisor_ = _divisor;
136  ve->vbo_ = _vbo;
137  addElement(ve);
138 
139  delete ve;
140 }
141 
142 void VertexDeclaration::addElement(unsigned int _type,
143  unsigned int _numElements,
144  VERTEX_USAGE _usage,
145  size_t _byteOffset,
146  const char* _shaderInputName,
147  unsigned int _divisor,
148  unsigned int _vbo)
149 {
150  VertexElement* ve = new VertexElement();
151 
152  ve->type_ = _type;
153  ve->numElements_ = _numElements;
154  ve->usage_ = _usage;
155  ve->shaderInputName_ = _shaderInputName;
156  ve->pointer_ = (void*)_byteOffset;
157  ve->divisor_ = _divisor;
158  ve->vbo_ = _vbo;
159 
160  addElement(ve);
161 
162  delete ve;
163 }
164 
165 
166 void VertexDeclaration::addElements(unsigned int _numElements, const VertexElement* _pElements)
167 {
168  elements_.reserve(elements_.size() + _numElements);
169 
170  for (unsigned int i = 0; i < _numElements; ++i)
171  {
172  VertexElement tmp = _pElements[i];
173  updateShaderInputName(&tmp);
174  elements_.push_back(tmp);
175  }
176 
177  updateOffsets();
178 
179  if (!strideUserDefined_)
180  {
181  // recompute vertex stride from declaration (based on last element)
182 
183  unsigned int n = getNumElements();
184 
185  if (n)
186  {
187  // stride = offset of last element + sizeof last element - offset of first element
188 
189  // union instead of reinterpret_cast for cross-platform compatibility
190  union ptr2uint
191  {
192  unsigned long u;
193  const void* p;
194  } lastOffset, firstOffset;
195 
196 
197 
198  std::map<unsigned int, VertexElement*> vboFirstElements;
199  std::map<unsigned int, VertexElement*> vboLastElements;
200 
201  for (unsigned int i = 0; i < n; ++i)
202  {
203  if (vboFirstElements.find(elements_[i].vbo_) == vboFirstElements.end())
204  vboFirstElements[elements_[i].vbo_] = &elements_[i];
205 
206  vboLastElements[elements_[i].vbo_] = &elements_[i];
207  }
208 
209 
210  for (std::map<unsigned int, VertexElement*>::iterator it = vboFirstElements.begin(); it != vboFirstElements.end(); ++it)
211  {
212  VertexElement* lastElement = vboLastElements[it->first];
213  firstOffset.p = it->second->pointer_;
214  lastOffset.p = lastElement->pointer_;
215 
216  vertexStridesVBO_[it->first] = static_cast<unsigned int>(lastOffset.u + getElementSize( lastElement ) - firstOffset.u);
217  }
218 
219  vertexStride_ = vertexStridesVBO_.begin()->second;
220  }
221  }
222 
223 }
224 
225 
226 // union instead of reinterpret_cast for cross-platform compatibility, must be global for use in std::map
228 {
229  unsigned long u;
230  const void* p;
231 };
232 
234 {
235  unsigned int numElements = getNumElements();
236 
237  if (!numElements) return;
238 
239 
240  // separate offsets for each vbo
241 
242  std::map<unsigned int, VertexDeclaration_ptr2uint> vboOffsets;
243  std::map<unsigned int, VertexElement*> vboPrevElements;
244 
245  for (unsigned int i = 0; i < numElements; ++i)
246  {
247  if (vboOffsets.find(elements_[i].vbo_) == vboOffsets.end())
248  {
249  vboOffsets[elements_[i].vbo_].p = elements_[i].pointer_;
250  vboPrevElements[elements_[i].vbo_] = &elements_[i];
251  }
252  }
253 
254  for (unsigned int i = 0; i < numElements; ++i)
255  {
256  VertexElement* el = &elements_[i];
257 
258  bool updateOffset = false;
259 
260  if (el->pointer_)
261  {
262  vboOffsets[el->vbo_].p = el->pointer_;
263  vboPrevElements[el->vbo_] = el;
264  }
265  else
266  {
267  VertexElement* prevEl = vboPrevElements[el->vbo_];
268  if (prevEl != el)
269  {
270  updateOffset = true;
271  vboOffsets[el->vbo_].u += getElementSize(prevEl);
272  }
273  vboPrevElements[el->vbo_] = el;
274  }
275 
276  if (updateOffset)
277  el->pointer_ = vboOffsets[el->vbo_].p;
278  }
279 }
280 
281 
283 {
284  if (!_pElem->shaderInputName_)
285  {
286  assert(_pElem->usage_ != VERTEX_USAGE_SHADER_INPUT);
287 
288  const char* sz = "";
289 
290  switch (_pElem->usage_)
291  {
292  case VERTEX_USAGE_POSITION: sz = "inPosition"; break;
293  case VERTEX_USAGE_NORMAL: sz = "inNormal"; break;
294  case VERTEX_USAGE_TEXCOORD: sz = "inTexCoord"; break;
295  case VERTEX_USAGE_COLOR: sz = "inColor"; break;
296  case VERTEX_USAGE_BLENDWEIGHTS: sz = "inBlendWeights"; break;
297  case VERTEX_USAGE_BLENDINDICES: sz = "inBlendIndices"; break;
298 
299  default:
300  std::cerr << "VertexDeclaration::updateShaderInputName - unknown vertex usage - " << _pElem->usage_ << std::endl;
301  break;
302  }
303 
304  _pElem->shaderInputName_ = sz;
305  }
306 }
307 
308 
310 {
311  return elements_.size();
312 }
313 
314 
315 size_t VertexDeclaration::getGLTypeSize(unsigned int _type)
316 {
317  size_t size = 0;
318 
319  switch (_type)
320  {
321  case GL_DOUBLE:
322  size = 8; break;
323 
324  case GL_FLOAT:
325  case GL_UNSIGNED_INT:
326  case GL_INT:
327  size = 4; break;
328 
329 // case GL_HALF_FLOAT_ARB:
330  case 0x140B: // = GL_HALF_FLOAT_ARB
331  case GL_SHORT:
332  case GL_UNSIGNED_SHORT:
333  size = 2; break;
334 
335  case GL_BYTE:
336  case GL_UNSIGNED_BYTE:
337  size = 1; break;
338 
339  default:
340  std::cerr << "VertexDeclaration::getElementSize - unknown type - " << _type << std::endl;
341  break;
342  }
343 
344  return size;
345 }
346 
347 
348 
350 {
351  return _pElement ? getGLTypeSize(_pElement->type_) * _pElement->numElements_ : 0;
352 }
353 
354 
355 
356 
357 
358 
359 
360 
362 {
363  unsigned int numElements = getNumElements();
364  unsigned int vertexStride = getVertexStride();
365 
366  for (unsigned int i = 0; i < numElements; ++i)
367  {
368  const VertexElement* pElem = getElement(i);
369 
370  switch (pElem->usage_)
371  {
373  {
374  ACG::GLState::vertexPointer(pElem->numElements_, pElem->type_, vertexStride, pElem->pointer_);
375  ACG::GLState::enableClientState(GL_VERTEX_ARRAY);
376  } break;
377 
378  case VERTEX_USAGE_COLOR:
379  {
380  ACG::GLState::colorPointer(pElem->numElements_, pElem->type_, vertexStride, pElem->pointer_);
381  ACG::GLState::enableClientState(GL_COLOR_ARRAY);
382  } break;
383 
385  {
386  glClientActiveTexture(GL_TEXTURE0);
387  ACG::GLState::texcoordPointer(pElem->numElements_, pElem->type_, vertexStride, pElem->pointer_);
388  ACG::GLState::enableClientState(GL_TEXTURE_COORD_ARRAY);
389  } break;
390 
391  case VERTEX_USAGE_NORMAL:
392  {
393  assert(pElem->numElements_ == 3);
394 
395  ACG::GLState::normalPointer(pElem->type_, vertexStride, pElem->pointer_);
396  ACG::GLState::enableClientState(GL_NORMAL_ARRAY);
397  } break;
398 
399  default: break;
400  }
401 
402 
403  }
404 }
405 
406 
408 {
409  unsigned int numElements = getNumElements();
410 
411  for (unsigned int i = 0; i < numElements; ++i)
412  {
413  const VertexElement* pElem = getElement(i);
414 
415  switch (pElem->usage_)
416  {
417  case VERTEX_USAGE_POSITION: ACG::GLState::disableClientState(GL_VERTEX_ARRAY); break;
418  case VERTEX_USAGE_COLOR: ACG::GLState::disableClientState(GL_COLOR_ARRAY); break;
419  case VERTEX_USAGE_TEXCOORD: ACG::GLState::disableClientState(GL_TEXTURE_COORD_ARRAY); break;
420  case VERTEX_USAGE_NORMAL: ACG::GLState::disableClientState(GL_NORMAL_ARRAY); break;
421 
422  default: break;
423  }
424  }
425 }
426 
427 
428 
430 {
431  assert(_prog);
432 
433  // setup correct attribute locations as specified
434 
435  unsigned int numElements = getNumElements();
436 
437  for (unsigned int i = 0; i < numElements; ++i)
438  {
439  unsigned int vertexStride = getVertexStride(i);
440 
441  const VertexElement* pElem = &elements_[i];
442 
443  int loc = _prog->getAttributeLocation(pElem->shaderInputName_);
444 
445  if (loc != -1)
446  {
447  // default: map integers to [-1, 1] or [0, 1] range
448  // exception: blend indices for gpu skinning eventually
449  GLboolean normalizeElem = GL_TRUE;
450 
451  if (pElem->usage_ == VERTEX_USAGE_BLENDINDICES)
452  normalizeElem = GL_FALSE;
453 
454  GLint curVBO = 0;
455  if (pElem->vbo_)
456  {
457  glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &curVBO);
458  glBindBuffer(GL_ARRAY_BUFFER, pElem->vbo_);
459  }
460 
461  glVertexAttribPointer(loc, pElem->numElements_, pElem->type_, normalizeElem, vertexStride, pElem->pointer_);
462 
463  if (supportsInstancedArrays())
464  {
465  if( openGLVersionTest(3,3) )
466  {
467  glVertexAttribDivisor(loc, pElem->divisor_);
468  }
469  else
470  {
471  glVertexAttribDivisorARB(loc, pElem->divisor_);
472  }
473  }
474  else if (pElem->divisor_)
475  std::cerr << "error: VertexDeclaration::activateShaderPipeline - instanced arrays not supported by gpu!" << std::endl;
476 
477  glEnableVertexAttribArray(loc);
478 
479  if (curVBO)
480  glBindBuffer(GL_ARRAY_BUFFER, curVBO);
481  }
482  }
483 }
484 
485 
487 {
488  assert(_prog);
489 
490  unsigned int numElements = getNumElements();
491 
492  for (unsigned int i = 0; i < numElements; ++i)
493  {
494  const VertexElement* pElem = &elements_[i];
495 
496  int loc = _prog->getAttributeLocation(pElem->shaderInputName_);
497 
498  if (loc != -1)
499  {
500  glDisableVertexAttribArray(loc);
501 
502  if (supportsInstancedArrays() && pElem->divisor_)
503  {
504  if( openGLVersionTest(3,3) )
505  {
506  glVertexAttribDivisor(loc, 0);
507  }
508  else
509  {
510  glVertexAttribDivisorARB(loc, 0);
511  }
512  }
513  }
514  }
515 }
516 
517 
518 
519 const VertexElement* VertexDeclaration::getElement(unsigned int i) const
520 {
521  return &elements_[i];
522 }
523 
524 
526 {
527  for (size_t i = 0; i < elements_.size(); ++i)
528  if (elements_[i].usage_ == _usage)
529  return int(i);
530 
531  return -1;
532 }
533 
535 {
536  int eid = findElementIdByUsage(_usage);
537 
538  if (eid >= 0)
539  return getElement((unsigned int)eid);
540 
541  return 0;
542 }
543 
544 
545 unsigned int VertexDeclaration::getVertexStride(unsigned int i) const
546 {
547  if (strideUserDefined_)
548  return vertexStride_;
549 
550  const VertexElement* element = getElement(i);
551 
552  if (element)
553  {
554  unsigned int vbo = getElement(i)->vbo_;
555  std::map<unsigned int, unsigned int>::const_iterator it = vertexStridesVBO_.find(vbo);
556 
557  return (it != vertexStridesVBO_.end()) ? it->second : vertexStride_;
558  }
559 
560  return 0;
561 }
562 
563 
564 void VertexDeclaration::setVertexStride(unsigned int _stride)
565 {
566  strideUserDefined_ = 1;
567  vertexStride_ = _stride;
568 }
569 
571 {
572  strideUserDefined_ = 0;
573  vertexStride_ = 0;
574 
575  elements_.clear();
576  vertexStridesVBO_.clear();
577 }
578 
579 
581 {
582  // maps VERTEX_USAGE -> string
583  const char* usageStrings[] =
584  {
585  "POSITION",
586  "NORMAL",
587  "TEXCOORD",
588  "COLOR",
589  "BLENDWEIGHTS",
590  "BLENDINDICES"
591  };
592 
593  QString result;
594 
595  QTextStream resultStrm(&result);
596  resultStrm << "stride = " << getVertexStride() << "\n";
597 
598 
599  for (unsigned int i = 0; i < getNumElements(); ++i)
600  {
601  const VertexElement* el = getElement(i);
602 
603  // convert element-type GLEnum to string
604  const char* typeString = "";
605 
606  switch (el->type_)
607  {
608  case GL_FLOAT: typeString = "GL_FLOAT"; break;
609  case GL_DOUBLE: typeString = "GL_DOUBLE"; break;
610 
611  case GL_INT: typeString = "GL_INT"; break;
612  case GL_UNSIGNED_INT: typeString = "GL_UNSIGNED_INT"; break;
613 
614  case GL_SHORT: typeString = "GL_SHORT"; break;
615  case GL_UNSIGNED_SHORT: typeString = "GL_UNSIGNED_SHORT"; break;
616 
617  case GL_BYTE: typeString = "GL_BYTE"; break;
618  case GL_UNSIGNED_BYTE: typeString = "GL_UNSIGNED_BYTE"; break;
619  default: typeString = "unknown"; break;
620  }
621 
622  // get usage in string form
623  const char* usage = (el->usage_ < VERTEX_USAGE_SHADER_INPUT) ? usageStrings[el->usage_] : el->shaderInputName_;
624 
625  resultStrm << "element " << i
626  << " - [type: " << typeString
627  << ", count: " << el->numElements_
628  << ", usage: " << usage
629  << ", shader-input: " << el->shaderInputName_
630  << ", offset: " << el->pointer_
631  << ", divisor: " << el->divisor_
632  << ", vbo: " << el->vbo_
633  << ", stride: " << getVertexStride(i)
634  << "]\n";
635  }
636 
637  return result;
638 }
639 
641 {
642  if (strideUserDefined_ != _other.strideUserDefined_ ||
643  vertexStride_ != _other.vertexStride_)
644  return false;
645 
646  if (elements_ != _other.elements_)
647  return false;
648 
649  if (vertexStridesVBO_ != _other.vertexStridesVBO_)
650  return false;
651 
652  return true;
653 }
654 
656 {
657  static int status_ = -1;
658 
659  if (status_ < 0) // core in 3.3
660  status_ = (checkExtensionSupported("GL_ARB_instanced_arrays") || openGLVersion(3,3)) ? 1 : 0;
661 
662  return status_ > 0;
663 }
664 
665 //=============================================================================
666 } // namespace ACG
667 //=============================================================================
void setVertexStride(unsigned int _stride)
const char * shaderInputName_
set shader input name, if usage_ = VERTEX_USAGE_USER_DEFINED otherwise this is set automatically...
defined by user via VertexElement::shaderInputName_
unsigned int numElements_
how many elements of type_
const void * pointer_
Offset in bytes to the first occurrence of this element in vertex buffer; Or address to vertex data i...
unsigned int getNumElements() const
Namespace providing different geometric functions concerning angles.
Class to define the vertex input layout.
void deactivateShaderPipeline(GLSL::Program *_prog) const
unsigned int vertexStride_
Offset in bytes between each vertex.
VERTEX_USAGE usage_
position, normal, shader input ..
static void texcoordPointer(GLint _size, GLenum _type, GLsizei _stride, const GLvoid *_pointer)
replaces glTexcoordPointer, supports locking
Definition: GLState.cc:2027
static void vertexPointer(GLint _size, GLenum _type, GLsizei _stride, const GLvoid *_pointer)
replaces glVertexPointer, supports locking
Definition: GLState.cc:1961
void updateShaderInputName(VertexElement *_pElem)
bool ACGDLLEXPORT openGLVersionTest(const int _major, const int _minor)
Definition: gl.hh:275
unsigned int getVertexStride(unsigned int i=0) const
std::map< unsigned int, unsigned int > vertexStridesVBO_
Map vbo to offset in bytes between each vertex in that vbo.
void activateFixedFunction() const
Description of one vertex element.
GLSL program class.
Definition: GLSLShader.hh:211
void setByteOffset(unsigned int _offset)
void addElement(const VertexElement *_pElement)
static void disableClientState(GLenum _cap)
replaces glDisableClientState, supports locking
Definition: GLState.cc:1584
static void enableClientState(GLenum _cap)
replaces glEnableClientState, supports locking
Definition: GLState.cc:1570
bool openGLVersion(const int _major, const int _minor, bool _verbose)
Definition: gl.cc:129
int findElementIdByUsage(VERTEX_USAGE _usage) const
static bool supportsInstancedArrays()
unsigned int getByteOffset() const
VERTEX_USAGE
-— input name in vertex shader ----—
void addElements(unsigned int _numElements, const VertexElement *_pElements)
bool operator==(const VertexDeclaration &_other) const
bool operator==(const VertexElement &_other) const
static void colorPointer(GLint _size, GLenum _type, GLsizei _stride, const GLvoid *_pointer)
replaces glColorPointer, supports locking
Definition: GLState.cc:2005
const VertexElement * getElement(unsigned int i) const
unsigned int vbo_
Explicit vbo source of this element, set to 0 if the buffer bound at the time of activating the decla...
static size_t getElementSize(const VertexElement *_pElement)
unsigned int type_
GL_FLOAT, GL_UNSIGNED_BYTE, GL_INT, ...
bool checkExtensionSupported(const std::string &_extension)
Definition: gl.cc:107
int getAttributeLocation(const char *_name)
Get location of the specified attribute.
Definition: GLSLShader.cc:702
unsigned int divisor_
For instanced rendering: Step rate describing how many instances are drawn before advancing to the ne...
void activateShaderPipeline(GLSL::Program *_prog) const
const VertexElement * findElementByUsage(VERTEX_USAGE _usage) const
static size_t getGLTypeSize(unsigned int _type)
void deactivateFixedFunction() const
int strideUserDefined_
Flag that indicates, whether the stride was set by user or derived automatically. ...
static void normalPointer(GLenum _type, GLsizei _stride, const GLvoid *_pointer)
replaces glNormalPointer, supports locking
Definition: GLState.cc:1983