Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit ee6e96a5 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Extended GeometryData API:

* Added optional picking information to GeometryData API
* GeometryRenderer processor renders the picking information into a separate texture if present
* geometrydata.h contains a stub for a possible rewrite of the API (more flexible buffer layout)
parent 26c8c5a6
......@@ -64,21 +64,19 @@ namespace campvis {
}
FaceGeometry* FaceGeometry::clone() const {
return new FaceGeometry(_vertices, _textureCoordinates, _colors, _normals);
FaceGeometry* toReturn = new FaceGeometry(_vertices, _textureCoordinates, _colors, _normals);
toReturn->setPickingInformation(_pickingInformation);
return toReturn;
}
size_t FaceGeometry::getLocalMemoryFootprint() const {
size_t sum = 0;
if (_verticesBuffer != 0)
sum += sizeof(tgt::BufferObject);
if (_texCoordsBuffer != 0)
sum += sizeof(tgt::BufferObject);
if (_colorsBuffer != 0)
sum += sizeof(tgt::BufferObject);
if (_normalsBuffer != 0)
sum += sizeof(tgt::BufferObject);
for (size_t i = 0; i < NUM_BUFFERS; ++i) {
if (_buffers[i] != nullptr)
sum += sizeof(tgt::BufferObject);
}
return sizeof(*this) + sum + (sizeof(tgt::vec3) * (_vertices.size() + _textureCoordinates.size() + _normals.size())) + (sizeof(tgt::vec4) * _colors.size());
return sizeof(*this) + sum + (sizeof(tgt::vec3) * (_vertices.size() + _textureCoordinates.size() + _normals.size())) + (sizeof(tgt::vec4) * (_colors.size() + _pickingInformation.size()));
}
size_t FaceGeometry::size() const {
......@@ -101,6 +99,16 @@ namespace campvis {
return _textureCoordinates;
}
const std::vector<tgt::col4>& FaceGeometry::getPickingInformation() const {
return _pickingInformation;
}
void FaceGeometry::setPickingInformation(const std::vector<tgt::col4>& pickingInformation) {
tgtAssert(pickingInformation.size() == 0 || pickingInformation.size() == _vertices.size(), "Number of picking informations does not match number of vertices!");
_pickingInformation = pickingInformation;
_buffersDirty = true;
}
const tgt::vec3& FaceGeometry::getFaceNormal() const {
return _faceNormal;
}
......@@ -124,6 +132,8 @@ namespace campvis {
vao.setVertexAttributePointer(2, _colorsBuffer);
if (_normalsBuffer)
vao.setVertexAttributePointer(3, _normalsBuffer);
if (_pickingBuffer)
vao.setVertexAttributePointer(4, _pickingBuffer);
LGL_ERROR;
glDrawArrays(mode, 0, static_cast<GLsizei>(_vertices.size()));
......@@ -150,6 +160,10 @@ namespace campvis {
_normalsBuffer = new tgt::BufferObject(tgt::BufferObject::ARRAY_BUFFER, tgt::BufferObject::USAGE_STATIC_DRAW);
_normalsBuffer->data(&_normals.front(), _normals.size() * sizeof(tgt::vec3), tgt::BufferObject::FLOAT, 3);
}
if (! _pickingInformation.empty()) {
_pickingBuffer = new tgt::BufferObject(tgt::BufferObject::ARRAY_BUFFER, tgt::BufferObject::USAGE_STATIC_DRAW);
_pickingBuffer->data(&_pickingInformation.front(), _pickingInformation.size() * sizeof(tgt::col4), tgt::BufferObject::UNSIGNED_BYTE, 4);
}
}
catch (tgt::Exception& e) {
LERROR("Error creating OpenGL Buffer objects: " << e.what());
......@@ -177,6 +191,7 @@ namespace campvis {
std::vector<tgt::vec3> verts, texCoords, norms;
std::vector<tgt::vec4> cols;
std::vector<tgt::col4> picks;
size_t lastIndex = _vertices.size() - 1;
float lastDistance = distanceToPlane(_vertices.back(), p, pNormal, epsilon);
......@@ -195,6 +210,8 @@ namespace campvis {
cols.push_back(tgt::mix(_colors[lastIndex], _colors[i], t));
if (!_normals.empty())
norms.push_back(tgt::mix(_normals[lastIndex], _normals[i], t));
if (!_pickingInformation.empty())
picks.push_back(_pickingInformation[i]);
}
// case 2: last vertex inside, this vertex outside clip region => clip
else if (lastDistance <= 0 && currrentDistance > 0) {
......@@ -207,6 +224,8 @@ namespace campvis {
cols.push_back(tgt::mix(_colors[lastIndex], _colors[i], t));
if (!_normals.empty())
norms.push_back(tgt::mix(_normals[lastIndex], _normals[i], t));
if (!_pickingInformation.empty())
picks.push_back(_pickingInformation[lastIndex]);
}
// case 1.2 + case 3: current vertix in front of plane => keep
......@@ -218,13 +237,17 @@ namespace campvis {
cols.push_back(_colors[i]);
if (!_normals.empty())
norms.push_back(_normals[i]);
if (!_pickingInformation.empty())
picks.push_back(_pickingInformation[i]);
}
lastIndex = i;
lastDistance = currrentDistance;
}
return FaceGeometry(verts, texCoords, cols, norms);
FaceGeometry toReturn(verts, texCoords, cols, norms);
toReturn.setPickingInformation(picks);
return toReturn;
}
tgt::Bounds FaceGeometry::getWorldBounds() const {
......@@ -238,6 +261,10 @@ namespace campvis {
return ! _textureCoordinates.empty();
}
bool FaceGeometry::hasPickingInformation() const {
return !_pickingInformation.empty();
}
void FaceGeometry::applyTransformationToVertices(const tgt::mat4& t) {
for (size_t i = 0; i < _vertices.size(); ++i) {
tgt::vec4 tmp = t * tgt::vec4(_vertices[i], 1.f);
......@@ -247,4 +274,5 @@ namespace campvis {
_buffersDirty = true;
}
}
\ No newline at end of file
......@@ -105,6 +105,18 @@ namespace campvis {
* \return _textureCoordinates
*/
const std::vector<tgt::vec3>& getTextureCoordinates() const;
/**
* The list of picking information colors, may be empty.
* \return _pickingInformation
*/
const std::vector<tgt::col4>& getPickingInformation() const;
/**
* Sets the picking information of this geometry to \a pickingInformation
* \param pickingInformation The new list of picking information for this geometry
*/
void setPickingInformation(const std::vector<tgt::col4>& pickingInformation);
/**
* The normal vector of this face.
......@@ -134,6 +146,8 @@ namespace campvis {
virtual tgt::Bounds getWorldBounds() const;
/// \see GeometryData::hasTextureCoordinates
virtual bool hasTextureCoordinates() const;
/// \see GeometryData::hasPickingInformation
virtual bool hasPickingInformation() const;
/// \see GeometryData::applyTransformationToVertices
virtual void applyTransformationToVertices(const tgt::mat4& t);
......@@ -149,6 +163,8 @@ namespace campvis {
std::vector<tgt::vec4> _colors; ///< The list of vertex colors, may be empty.
std::vector<tgt::vec3> _normals; ///< The list of vertex normals, may be empty.
std::vector<tgt::col4> _pickingInformation; ///< The list of picking information colors, max be empty.
tgt::vec3 _faceNormal; ///< The normal vector of this face.
static const std::string loggerCat_;
......
......@@ -37,20 +37,22 @@ namespace campvis {
GeometryData::GeometryData()
: AbstractData()
, _buffersDirty(true)
, _verticesBuffer(0)
, _texCoordsBuffer(0)
, _colorsBuffer(0)
, _normalsBuffer(0)
, _verticesBuffer(nullptr)
, _texCoordsBuffer(nullptr)
, _colorsBuffer(nullptr)
, _normalsBuffer(nullptr)
, _pickingBuffer(nullptr)
{
}
GeometryData::GeometryData(const GeometryData& rhs)
: AbstractData(rhs)
, _buffersDirty(true)
, _verticesBuffer(0)
, _texCoordsBuffer(0)
, _colorsBuffer(0)
, _normalsBuffer(0)
, _verticesBuffer(nullptr)
, _texCoordsBuffer(nullptr)
, _colorsBuffer(nullptr)
, _normalsBuffer(nullptr)
, _pickingBuffer(nullptr)
{
}
......@@ -74,21 +76,23 @@ namespace campvis {
void GeometryData::deleteBuffers() const {
for (int i = 0; i < NUM_BUFFERS; ++i) {
delete _buffers[i];
_buffers[i] = 0;
_buffers[i] = nullptr;
}
}
size_t GeometryData::getVideoMemoryFootprint() const {
size_t sum = 0;
if (_verticesBuffer != 0)
if (_verticesBuffer != nullptr)
sum += _verticesBuffer->getBufferSize();
if (_texCoordsBuffer != 0)
if (_texCoordsBuffer != nullptr)
sum += _texCoordsBuffer->getBufferSize();
if (_colorsBuffer != 0)
if (_colorsBuffer != nullptr)
sum += _colorsBuffer->getBufferSize();
if (_normalsBuffer != 0)
if (_normalsBuffer != nullptr)
sum += _normalsBuffer->getBufferSize();
if (_pickingBuffer != nullptr)
sum += _pickingBuffer->getBufferSize();
return sum;
}
......@@ -109,4 +113,8 @@ namespace campvis {
return _normalsBuffer;
}
const tgt::BufferObject* GeometryData::getPickingBuffer() const {
return _pickingBuffer;
}
}
\ No newline at end of file
......@@ -36,6 +36,37 @@ namespace tgt {
}
namespace campvis {
class CAMPVIS_CORE_API GeometryDataBase {
public:
/// Enumeration for defining semantics of stored buffer data
enum ElementSemantic {
VERTEX = 0, ///< Vextex data
TEXTURE_COORDINATE = 1, ///< Texture coordinate data
COLOR = 2, ///< Color data
NORMAL = 3, ///< Normal data
PICKING_INFORMATION = 4 ///< Picking information
};
/// Enumeration for defining the host data type of the element
enum ElementHostType {
UINT8,
UINT16,
UINT32,
FLOAT,
VEC2,
VEC3,
VEC4
};
};
template<GeometryDataBase::ElementSemantic SEMANTIC>
struct GeometryDataTraits {};
template<>
struct GeometryDataTraits<GeometryDataBase::VERTEX> {
typedef tgt::vec3 HostType;
};
/**
* Abstract base class for geometry data in CAMPVis.
*
......@@ -50,7 +81,7 @@ namespace campvis {
* - Vertex normals: Vertex attribute 3
*
*/
class CAMPVIS_CORE_API GeometryData : public AbstractData, public IHasWorldBounds {
class CAMPVIS_CORE_API GeometryData : public AbstractData, public GeometryDataBase, public IHasWorldBounds {
public:
/**
* Constructor
......@@ -91,12 +122,24 @@ namespace campvis {
*/
virtual tgt::Bounds getWorldBounds() const = 0;
template<GeometryDataBase::ElementSemantic SEMANTIC>
const std::vector<typename GeometryDataTraits<SEMANTIC>::HostType>* getElementData() const;
template<GeometryDataBase::ElementSemantic SEMANTIC>
void setElementData(std::vector<typename GeometryDataTraits<SEMANTIC>::HostType>* elementData);
/**
* Returns whether the geometry has texture coordinates.
* \return True if this geometry sets texture coordinates during rendering.
*/
virtual bool hasTextureCoordinates() const = 0;
/**
* Returns whether this geometry has picking information.
* \return True if this geometry sets picking information during rendering.
*/
virtual bool hasPickingInformation() const = 0;
/**
* Applies the transformation matrix \a t to each vertex of this geometry.
* \param t Transformation matrix to apply
......@@ -131,6 +174,13 @@ namespace campvis {
*/
const tgt::BufferObject* getNormalsBuffer() const;
/**
* Returns the Pointer to the OpenGL Buffer with the vertex normals.
* May be 0 if none are present or not yet created.
* \return _normalsBuffer
*/
const tgt::BufferObject* getPickingBuffer() const;
/// \see AbstractData::getVideoMemoryFootprint()
virtual size_t getVideoMemoryFootprint() const;
......@@ -140,10 +190,13 @@ namespace campvis {
*/
void deleteBuffers() const;
std::vector<void*> _elementPointers;
// mutable to support const lazy initialization
mutable bool _buffersDirty; ///< Flag whether the buffers are dirty (i.e. need to be (re)initialized)
enum { NUM_BUFFERS = 4 }; ///< Number of buffers in _buffers array
enum { NUM_BUFFERS = 5 }; ///< Number of buffers in _buffers array
union {
struct {
......@@ -151,6 +204,7 @@ namespace campvis {
mutable tgt::BufferObject* _texCoordsBuffer; ///< Pointer to the OpenGL Buffer with the vertex texture coordinates
mutable tgt::BufferObject* _colorsBuffer; ///< Pointer to the OpenGL Buffer with the vertex colors
mutable tgt::BufferObject* _normalsBuffer; ///< Pointer to the OpenGL Buffer with the vertex normals
mutable tgt::BufferObject* _pickingBuffer; ///< Pointer to the OpenGL Buffer with the picking information
};
mutable tgt::BufferObject* _buffers[NUM_BUFFERS]; ///< Array of all buffers
......@@ -161,6 +215,27 @@ namespace campvis {
static const std::string loggerCat_;
};
template<GeometryDataBase::ElementSemantic SEMANTIC>
const std::vector<typename GeometryDataTraits<SEMANTIC>::HostType>* GeometryData::getElementData() const {
if (_elementPointers.size() >= SEMANTIC) {
return static_cast< std::vector<typename GeometryDataTraits<SEMANTIC>::HostType>* >(_elementPointers[SEMANTIC]);
}
return nullptr;
}
template<GeometryDataBase::ElementSemantic SEMANTIC>
void GeometryData::setElementData(std::vector<typename GeometryDataTraits<SEMANTIC>::HostType>* elementData) {
if (_elementPointers.size() < SEMANTIC + 1)
_elementPointers.resize(SEMANTIC, nullptr);
void* oldPtr = _elementPointers[SEMANTIC];
if (oldPtr != elementData)
delete static_cast< std::vector<typename GeometryDataTraits<SEMANTIC>::HostType>* >(oldPtr);
_elementPointers[SEMANTIC] = elementData;
}
}
#endif // GEOMETRYDATA_H__
......@@ -114,6 +114,15 @@ namespace campvis {
return true;
}
bool GeometryDataCollection::hasPickingInformation() const {
for (size_t i = 0; i < _geometries.size(); ++i) {
if (! _geometries[i]->hasPickingInformation())
return false;
}
return true;
}
void GeometryDataCollection::applyTransformationToVertices(const tgt::mat4& t) {
for (size_t i = 0; i < _geometries.size(); ++i)
_geometries[i]->applyTransformationToVertices(t);
......@@ -136,5 +145,4 @@ namespace campvis {
return sum;
}
}
\ No newline at end of file
......@@ -109,6 +109,12 @@ namespace campvis {
*/
virtual bool hasTextureCoordinates() const;
/**
* Returns whether all geometries of the collection have picking information.
* \return True if all geometries of the collection set picking information during rendering.
*/
virtual bool hasPickingInformation() const;
/**
* Applies the transformation matrix \a t to each vertex of this geometry.
* \param t Transformation matrix to apply
......
......@@ -84,6 +84,7 @@ namespace campvis {
_textureCoordinates = rhs._textureCoordinates;
_colors = rhs._colors;
_normals = rhs._normals;
_pickingInformation = rhs._pickingInformation;
// delete old VBOs and null pointers
deleteIndicesBuffer();
......@@ -91,23 +92,28 @@ namespace campvis {
return *this;
}
const std::vector<tgt::col4>& IndexedMeshGeometry::getPickingInformation() const {
return _pickingInformation;
}
void IndexedMeshGeometry::setPickingInformation(const std::vector<tgt::col4>& pickingInformation) {
tgtAssert(pickingInformation.size() == 0 || pickingInformation.size() == _vertices.size(), "Number of picking informations does not match number of vertices!");
_pickingInformation = pickingInformation;
_buffersDirty = true;
}
IndexedMeshGeometry* IndexedMeshGeometry::clone() const {
return new IndexedMeshGeometry(_indices, _vertices, _textureCoordinates, _colors, _normals);
IndexedMeshGeometry* toReturn = new IndexedMeshGeometry(_indices, _vertices, _textureCoordinates, _colors, _normals);
toReturn->setPickingInformation(_pickingInformation);
return toReturn;
}
size_t IndexedMeshGeometry::getLocalMemoryFootprint() const {
size_t sum = 0;
if (_indicesBuffer != 0)
sum += sizeof(tgt::BufferObject);
if (_verticesBuffer != 0)
sum += sizeof(tgt::BufferObject);
if (_texCoordsBuffer != 0)
sum += sizeof(tgt::BufferObject);
if (_colorsBuffer != 0)
sum += sizeof(tgt::BufferObject);
if (_normalsBuffer != 0)
sum += sizeof(tgt::BufferObject);
for (size_t i = 0; i < NUM_BUFFERS; ++i) {
if (_buffers[i] != nullptr)
sum += sizeof(tgt::BufferObject);
}
return sizeof(*this) + sum + (sizeof(size_t) * _indices.size()) + (sizeof(tgt::vec3) * (_vertices.size() + _textureCoordinates.size() + _normals.size())) + (sizeof(tgt::vec4) * _colors.size());
}
......@@ -136,6 +142,8 @@ namespace campvis {
vao.setVertexAttributePointer(2, _colorsBuffer);
if (_normalsBuffer)
vao.setVertexAttributePointer(3, _normalsBuffer);
if (_pickingBuffer)
vao.setVertexAttributePointer(4, _pickingBuffer);
vao.bindIndexBuffer(_indicesBuffer);
glDrawElements(mode, static_cast<GLsizei>(_indices.size()), GL_UNSIGNED_SHORT, 0);
......@@ -166,6 +174,10 @@ namespace campvis {
_normalsBuffer = new tgt::BufferObject(tgt::BufferObject::ARRAY_BUFFER, tgt::BufferObject::USAGE_STATIC_DRAW);
_normalsBuffer->data(&_normals.front(), _normals.size() * sizeof(tgt::vec3), tgt::BufferObject::FLOAT, 3);
}
if (! _pickingInformation.empty()) {
_pickingBuffer = new tgt::BufferObject(tgt::BufferObject::ARRAY_BUFFER, tgt::BufferObject::USAGE_STATIC_DRAW);
_pickingBuffer->data(&_pickingInformation.front(), _pickingInformation.size() * sizeof(tgt::col4), tgt::BufferObject::UNSIGNED_BYTE, 4);
}
}
catch (tgt::Exception& e) {
LERROR("Error creating OpenGL Buffer objects: " << e.what());
......@@ -194,6 +206,10 @@ namespace campvis {
return ! _textureCoordinates.empty();
}
bool IndexedMeshGeometry::hasPickingInformation() const {
return _pickingInformation.empty();
}
void IndexedMeshGeometry::applyTransformationToVertices(const tgt::mat4& t) {
for (size_t i = 0; i < _vertices.size(); ++i) {
tgt::vec4 tmp = t * tgt::vec4(_vertices[i], 1.f);
......@@ -203,5 +219,4 @@ namespace campvis {
_buffersDirty = true;
}
}
\ No newline at end of file
......@@ -22,8 +22,8 @@
//
// ================================================================================================
#ifndef INDEXEDIndexedMeshGeometry_H__
#define INDEXEDIndexedMeshGeometry_H__
#ifndef INDEXEDMESHGEOMETRY_H__
#define INDEXEDMESHGEOMETRY_H__
#include "tgt/bounds.h"
#include "tgt/vector.h"
......@@ -79,6 +79,20 @@ namespace campvis {
*/
IndexedMeshGeometry& operator=(const IndexedMeshGeometry& rhs);
/**
* The list of picking information colors, may be empty.
* \return _pickingInformation
*/
const std::vector<tgt::col4>& getPickingInformation() const;
/**
* Sets the picking information of this geometry to \a pickingInformation
* \param pickingInformation The new list of picking information for this geometry
*/
void setPickingInformation(const std::vector<tgt::col4>& pickingInformation);
/// \see AbstractData::clone()
virtual IndexedMeshGeometry* clone() const;
......@@ -99,6 +113,8 @@ namespace campvis {
virtual tgt::Bounds getWorldBounds() const;
/// \see GeometryData::hasTextureCoordinates
virtual bool hasTextureCoordinates() const;
/// \see GeometryData::hasPickingInformation
virtual bool hasPickingInformation() const;
/// \see GeometryData::applyTransformationToVertices
virtual void applyTransformationToVertices(const tgt::mat4& t);
......@@ -118,6 +134,8 @@ namespace campvis {
std::vector<tgt::vec4> _colors; ///< The list of vertex colors, may be empty.
std::vector<tgt::vec3> _normals; ///< The list of vertex normals, may be empty.
std::vector<tgt::col4> _pickingInformation; ///< The list of picking information colors, max be empty.
mutable tgt::BufferObject* _indicesBuffer;
static const std::string loggerCat_;
......@@ -125,4 +143,4 @@ namespace campvis {
}
#endif // INDEXEDIndexedMeshGeometry_H__
#endif // INDEXEDMESHGEOMETRY_H__
......@@ -56,14 +56,10 @@ namespace campvis {
for (std::vector<FaceGeometry>::const_iterator it = _faces.begin(); it != _faces.end(); ++it)
sum += it->getLocalMemoryFootprint();
if (_verticesBuffer != 0)
sum += sizeof(tgt::BufferObject);
if (_texCoordsBuffer != 0)
sum += sizeof(tgt::BufferObject);
if (_colorsBuffer != 0)
sum += sizeof(tgt::BufferObject);
if (_normalsBuffer != 0)
sum += sizeof(tgt::BufferObject);
for (size_t i = 0; i < NUM_BUFFERS; ++i) {
if (_buffers[i] != nullptr)
sum += sizeof(tgt::BufferObject);
}
return sizeof(*this) + sum;
}
......@@ -96,6 +92,8 @@ namespace campvis {
vao.setVertexAttributePointer(2, _colorsBuffer);
if (_normalsBuffer)
vao.setVertexAttributePointer(3, _normalsBuffer);
if (_pickingBuffer)
vao.setVertexAttributePointer(4, _pickingBuffer);
LGL_ERROR;
GLint startIndex = 0;
......@@ -117,6 +115,7 @@ namespace campvis {
bool createTexCoordsBuffer = true;
bool createColorsBuffer = true;
bool createNormalsBuffer = true;
bool createPickingBuffer = true;
size_t totalVertices = 0;
// Check which buffers are to create. Meanwhile calculate the total number of vertices:
......@@ -126,6 +125,7 @@ namespace campvis {
createTexCoordsBuffer &= !(it->getTextureCoordinates().empty());
createColorsBuffer &= !(it->getColors().empty());
createNormalsBuffer &= !(it->getNormals().empty());
createPickingBuffer &= !(it->getPickingInformation().empty());
#ifdef CAMPVIS_DEBUG
if (!createTexCoordsBuffer && !(it->getTextureCoordinates().empty()))
......@@ -134,6 +134,8 @@ namespace campvis {
LWARNING("Presence of colors in faces not consistent, not generating colors VBO!");
if (!createNormalsBuffer && !(it->getNormals().empty()))
LWARNING("Presence of normals in faces not consistent, not generating normals VBO!");
if (!createPickingBuffer && !(it->getPickingInformation().empty()))
LWARNING("Presence of picking information in faces not consistent, not generating normals VBO!");
#endif
}
......@@ -154,6 +156,10 @@ namespace campvis {
_normalsBuffer = new tgt::BufferObject(tgt::BufferObject::ARRAY_BUFFER, tgt::BufferObject::USAGE_STATIC_DRAW);
_normalsBuffer->data(0, totalVertices * sizeof(tgt::vec3), tgt::BufferObject::FLOAT, 3);
}
if (createPickingBuffer) {
_pickingBuffer = new tgt::BufferObject(tgt::BufferObject::ARRAY_BUFFER, tgt::BufferObject::USAGE_STATIC_DRAW);
_pickingBuffer->data(0, totalVertices * sizeof(tgt::vec4), tgt::BufferObject::FLOAT, 4);
}
// Now start filling the VBOs with data, one face at a time...
size_t startIndex = 0;
......@@ -170,6 +176,8 @@ namespace campvis {
_colorsBuffer->subdata(startIndex * sizeof(tgt::vec4), &(it->getColors().front()), numVertices * sizeof(tgt::vec4));
if (createNormalsBuffer)
_normalsBuffer->subdata(startIndex * sizeof(tgt::vec3), &(it->getNormals().front()), numVertices * sizeof(tgt::vec3));