Notice to GitKraken users: A vulnerability has been found in the SSH key generation of GitKraken versions 7.6.0 to 8.0.0 (https://www.gitkraken.com/blog/weak-ssh-key-fix). If you use GitKraken and have generated a SSH key using one of these versions, please remove it both from your local workstation and from your LRZ GitLab profile.

21.10.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

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));
if (createPickingBuffer)