/** * @file PrimitiveRenderer.cpp * A geometric primitive renderer. * * @author: Norman Crafts * @copyright 2014, High Fidelity, Inc. All rights reserved. */ #include #include "InterfaceConfig.h" #include "OctreeElement.h" #include "PrimitiveRenderer.h" Primitive::Primitive() { } Primitive::~Primitive() { } // Simple dispatch between API and SPI VertexElementList const & Primitive::vertexElements() const { return vVertexElements(); } VertexElementIndexList& Primitive::vertexElementIndices() { return vVertexElementIndices(); } TriElementList& Primitive::triElements() { return vTriElements(); } void Primitive::releaseVertexElements() { vReleaseVertexElements(); } int Primitive::getMemoryUsage() { return vGetMemoryUsage(); } Cube::Cube( float x, float y, float z, float s, unsigned char r, unsigned char g, unsigned char b, unsigned char faceExclusions ) : _cpuMemoryUsage(0) { initialize(x, y, z, s, r, g, b, faceExclusions); } Cube::~Cube() { terminate(); } void Cube::initialize( float x, float y, float z, float s, unsigned char r, unsigned char g, unsigned char b, unsigned char faceExclusions ) { initializeVertices(x, y, z, s, r, g, b, faceExclusions); initializeTris(faceExclusions); } void Cube::terminate() { terminateTris(); terminateVertices(); } void Cube::initializeVertices( float x, float y, float z, float s, unsigned char r, unsigned char g, unsigned char b, unsigned char faceExclusions ) { for (int i = 0; i < _sNumVerticesPerCube; i++) { // Check whether the vertex is necessary for the faces indicated by faceExclusions bit mask. if (~faceExclusions & _sFaceIndexToHalfSpaceMask[i >> 2]) { //if (~0x00 & _sFaceIndexToHalfSpaceMask[i >> 2]) { //if (faceExclusions & _sFaceIndexToHalfSpaceMask[i >> 2]) { VertexElement* v = new VertexElement(); if (v) { // Construct vertex position v->position.x = x + s * _sVertexIndexToConstructionVector[i][0]; v->position.y = y + s * _sVertexIndexToConstructionVector[i][1]; v->position.z = z + s * _sVertexIndexToConstructionVector[i][2]; // Construct vertex normal v->normal.x = _sVertexIndexToNormalVector[i >> 2][0]; v->normal.y = _sVertexIndexToNormalVector[i >> 2][1]; v->normal.z = _sVertexIndexToNormalVector[i >> 2][2]; // Construct vertex color //#define FALSE_COLOR #ifndef FALSE_COLOR v->color.r = r; v->color.g = g; v->color.b = b; v->color.a = 255; #else static unsigned char falseColor[6][3] = { 192, 0, 0, // Bot 0, 192, 0, // Top 0, 0, 192, // Right 192, 0, 192, // Left 192, 192, 0, // Near 192, 192, 192 // Far }; v->color.r = falseColor[i >> 2][0]; v->color.g = falseColor[i >> 2][1]; v->color.b = falseColor[i >> 2][2]; v->color.a = 255; #endif // Add vertex element to list _vertices.push_back(v); _cpuMemoryUsage += sizeof(VertexElement); _cpuMemoryUsage += sizeof(VertexElement*); } } } } void Cube::terminateVertices() { VertexElementList::iterator it = _vertices.begin(); VertexElementList::iterator end = _vertices.end(); for ( ; it != end; ++it) { delete *it; } _cpuMemoryUsage -= _vertices.size() * (sizeof(VertexElement) + sizeof(VertexElement*)); _vertices.clear(); } void Cube::initializeTris( unsigned char faceExclusions ) { int index = 0; for (int i = 0; i < _sNumFacesPerCube; i++) { // Check whether the vertex is necessary for the faces indicated by faceExclusions bit mask. // uncomment this line to exclude shared faces: if (~faceExclusions & _sFaceIndexToHalfSpaceMask[i]) { // uncomment this line to load all faces: if (~0x00 & _sFaceIndexToHalfSpaceMask[i]) { // uncomment this line to include shared faces: if (faceExclusions & _sFaceIndexToHalfSpaceMask[i]) { int start = index; // Create the triangulated face, two tris, six indices referencing four vertices, both // with cw winding order, such that: // A-B // |\| // D-C // Store triangle ABC TriElement *tri = new TriElement(); if (tri) { tri->indices[0] = index++; tri->indices[1] = index++; tri->indices[2] = index; // Add tri element to list _tris.push_back(tri); _cpuMemoryUsage += sizeof(TriElement); _cpuMemoryUsage += sizeof(TriElement*); } // Now store triangle ACD tri = new TriElement(); if (tri) { tri->indices[0] = start; tri->indices[1] = index++; tri->indices[2] = index++; // Add tri element to list _tris.push_back(tri); _cpuMemoryUsage += sizeof(TriElement); _cpuMemoryUsage += sizeof(TriElement*); } } } } void Cube::terminateTris() { TriElementList::iterator it = _tris.begin(); TriElementList::iterator end = _tris.end(); for ( ; it != end; ++it) { delete *it; } _cpuMemoryUsage -= _tris.size() * (sizeof(TriElement) + sizeof(TriElement*)); _tris.clear(); } VertexElementList const & Cube::vVertexElements() const { return _vertices; } VertexElementIndexList& Cube::vVertexElementIndices() { return _vertexIndices; } TriElementList& Cube::vTriElements() { return _tris; } void Cube::vReleaseVertexElements() { terminateVertices(); } int Cube::vGetMemoryUsage() { return _cpuMemoryUsage; } unsigned char Cube::_sFaceIndexToHalfSpaceMask[6] = { OctreeElement::HalfSpace::Bottom, OctreeElement::HalfSpace::Top, OctreeElement::HalfSpace::Right, OctreeElement::HalfSpace::Left, OctreeElement::HalfSpace::Near, OctreeElement::HalfSpace::Far, }; // Construction vectors ordered such that the vertices of each face are // clockwise in a right-handed coordinate system with B-L-N at 0,0,0. float Cube::_sVertexIndexToConstructionVector[24][3] = { // Bottom { 0,0,0 }, { 1,0,0 }, { 1,0,1 }, { 0,0,1 }, // Top { 0,1,0 }, { 0,1,1 }, { 1,1,1 }, { 1,1,0 }, // Right { 1,0,0 }, { 1,1,0 }, { 1,1,1 }, { 1,0,1 }, // Left { 0,0,0 }, { 0,0,1 }, { 0,1,1 }, { 0,1,0 }, // Near { 0,0,0 }, { 0,1,0 }, { 1,1,0 }, { 1,0,0 }, // Far { 0,0,1 }, { 1,0,1 }, { 1,1,1 }, { 0,1,1 }, }; // Normals for a right-handed coordinate system float Cube::_sVertexIndexToNormalVector[6][3] = { { 0,-1, 0 }, // Bottom { 0, 1, 0 }, // Top { 1, 0, 0 }, // Right { -1, 0, 0 }, // Left { 0, 0,-1 }, // Near { 0, 0, 1 }, // Far }; Renderer::Renderer() { } Renderer::~Renderer() { } // Simple dispatch between API and SPI int Renderer::add( Primitive* primitive ) { return vAdd(primitive); } void Renderer::remove( int id ) { vRemove(id); } void Renderer::release() { vRelease(); } void Renderer::render() { vRender(); } int Renderer::getMemoryUsage() { return vGetMemoryUsage(); } int Renderer::getMemoryUsageGPU() { return vGetMemoryUsageGPU(); } PrimitiveRenderer::PrimitiveRenderer( int maxCount ) : _maxCount(maxCount), _vertexElementCount(0), _maxVertexElementCount(0), _triElementCount(0), _maxTriElementCount(0), _primitiveCount(0), _triBufferId(0), _vertexBufferId(0), _gpuMemoryUsage(0), _cpuMemoryUsage(0) { initialize(); } PrimitiveRenderer::~PrimitiveRenderer() { terminate(); } void PrimitiveRenderer::initialize() { initializeGL(); initializeBookkeeping(); } void PrimitiveRenderer::initializeGL() { glGenBuffers(1, &_triBufferId); glGenBuffers(1, &_vertexBufferId); // Set up the element array buffer containing the index ids _maxTriElementCount = _maxCount * _sIndicesPerTri * 2; int size = _maxTriElementCount * sizeof(GLint); _gpuMemoryUsage += size; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _triBufferId); glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Set up the array buffer in the form of array of structures // I chose AOS because it maximizes the amount of data tranferred // by a single glBufferSubData call. _maxVertexElementCount = _maxCount * 4; size = _maxVertexElementCount * sizeof(VertexElement); _gpuMemoryUsage += size; glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId); glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); // Reserve the the first element for the degenerate case _vertexElementCount = 1; _triElementCount = 1; // Initialize the first tri element in the buffer to all zeros, the // degenerate case deconstructTriElement(0); // Initialize the first vertex element in the buffer to all zeros, the // degenerate case deconstructVertexElement(0); GLint err = glGetError(); } void PrimitiveRenderer::initializeBookkeeping() { // Start primitive count at one, because zero is reserved for the degenerate triangle _primitives.resize(_maxCount + 1); _primitiveCount = 1; _cpuMemoryUsage = sizeof(PrimitiveRenderer) + _primitives.size() * sizeof(Primitive *); } void PrimitiveRenderer::terminate() { terminateBookkeeping(); terminateGL(); } void PrimitiveRenderer::terminateGL() { glDeleteBuffers(1, &_vertexBufferId); glDeleteBuffers(1, &_triBufferId); GLint err = glGetError(); } void PrimitiveRenderer::terminateBookkeeping() { for (int i = _primitiveCount + 1; --i > 0; ) { Primitive* primitive = _primitives[i]; if (primitive) { _cpuMemoryUsage -= primitive->getMemoryUsage(); _primitives[i] = 0; delete primitive; } } // Drain all of the queues, stop updating the counters while (_availableVertexElementIndex.remove() != 0) ; while (_availableTriElementIndex.remove() != 0) ; while (_deconstructTriElementIndex.remove() != 0) ; _vertexElementCount = 1; _triElementCount = 1; _primitiveCount = 1; } void PrimitiveRenderer::constructElements( Primitive* primitive ) { // Load vertex elements VertexElementIndexList& vertexElementIndexList = primitive->vertexElementIndices(); VertexElementList const & vertices = primitive->vertexElements(); { VertexElementList::const_iterator it = vertices.begin(); VertexElementList::const_iterator end = vertices.end(); for ( ; it != end; ++it ) { int index = getAvailableVertexElementIndex(); if (index != 0) { vertexElementIndexList.push_back(index); VertexElement* vertex = *it; transferVertexElement(index, vertex); } else { break; } } } // Load tri elements if (vertexElementIndexList.size() == vertices.size()) { TriElementList& tris = primitive->triElements(); TriElementList::iterator it = tris.begin(); TriElementList::iterator end = tris.end(); for ( ; it != end; ++it) { TriElement* tri = *it; int index = getAvailableTriElementIndex(); if (index != 0) { int k; k = tri->indices[0]; tri->indices[0] = vertexElementIndexList[k]; k = tri->indices[1]; tri->indices[1] = vertexElementIndexList[k]; k = tri->indices[2]; tri->indices[2] = vertexElementIndexList[k]; tri->id = index; transferTriElement(index, tri->indices); } else { break; } } } else { // TODO: failure mode } } void PrimitiveRenderer::deconstructElements( Primitive* primitive ) { // Schedule the tri elements of the face for deconstruction { TriElementList& tris = primitive->triElements(); TriElementList::iterator it = tris.begin(); TriElementList::iterator end = tris.end(); for ( ; it != end; ++it) { TriElement* tri = *it; // Put the tri element index into decon queue _deconstructTriElementIndex.add(tri->id); } } // Return the vertex element index to the available queue, it is not necessary // to zero the data { VertexElementIndexList& vertexIndexList = primitive->vertexElementIndices(); VertexElementIndexList::iterator it = vertexIndexList.begin(); VertexElementIndexList::iterator end = vertexIndexList.end(); for ( ; it != end; ++it) { int index = *it; // Put the vertex element index into the available queue _availableVertexElementIndex.add(index); } } delete primitive; } int PrimitiveRenderer::getAvailablePrimitiveIndex() { // Check the available primitive index queue first for an available index. int index = _availablePrimitiveIndex.remove(); // Remember that the primitive index 0 is not used. if (index == 0) { // There are no primitive indices available from the queue, // make one up if (_primitiveCount < _maxCount) { index = _primitiveCount++; } } return index; } int PrimitiveRenderer::getAvailableVertexElementIndex() { // Check the available vertex element queue first for an available index. int index = _availableVertexElementIndex.remove(); // Remember that the vertex element 0 is used for degenerate triangles. if (index == 0) { // There are no vertex elements available from the queue, // grab one from the end of the list if (_vertexElementCount < _maxVertexElementCount) { index = _vertexElementCount++; } } return index; } int PrimitiveRenderer::getAvailableTriElementIndex() { // Check the deconstruct tri element queue first for an available index. int index = _deconstructTriElementIndex.remove(); // Remember that the tri element 0 is used for degenerate triangles. if (index == 0) { // There are no tri elements in the deconstruct tri element queue that are reusable. // Check the available tri element queue. index = _availableTriElementIndex.remove(); if (index == 0) { // There are no reusable tri elements available from any queue, // grab one from the end of the list if (_triElementCount < _maxTriElementCount) { index = _triElementCount++; } } } return index; } void PrimitiveRenderer::deconstructTriElement( int idx ) { // Set the tri element to the degenerate case. static int degenerate[3] = { 0, 0, 0 }; transferTriElement(idx, degenerate); } void PrimitiveRenderer::deconstructVertexElement( int idx ) { // Set the vertex element to the degenerate case. VertexElement degenerate; memset(°enerate, 0, sizeof(degenerate)); transferVertexElement(idx, °enerate); } void PrimitiveRenderer::transferVertexElement( int idx, VertexElement* vertex ) { glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId); glBufferSubData(GL_ARRAY_BUFFER, idx * sizeof(VertexElement), sizeof(VertexElement), vertex); glBindBuffer(GL_ARRAY_BUFFER, 0); } void PrimitiveRenderer::transferTriElement( int idx, int tri[3] ) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _triBufferId); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, idx * _sBytesPerTriElement, _sBytesPerTriElement, tri); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } int PrimitiveRenderer::vAdd( Primitive* primitive ) { QMutexLocker lock(&_guard); int index = getAvailablePrimitiveIndex(); if (index != 0) { try { // Take ownership of primitive, including responsibility // for destruction _primitives[index] = primitive; _constructPrimitiveIndex.add(index); _cpuMemoryUsage += primitive->getMemoryUsage(); } catch(...) { // STL failed, recycle the index _availablePrimitiveIndex.add(index); index = 0; } } return index; } void PrimitiveRenderer::vRemove( int index ) { try { QMutexLocker lock(&_guard); // Locate and remove the primitive by id in the associative map Primitive* primitive = _primitives[index]; if (primitive) { _primitives[index] = 0; _cpuMemoryUsage -= primitive->getMemoryUsage(); deconstructElements(primitive); _availablePrimitiveIndex.add(index); } } catch(...) { // STL failed } } void PrimitiveRenderer::vRelease() { QMutexLocker lock(&_guard); terminateBookkeeping(); } void PrimitiveRenderer::vRender() { int id; QMutexLocker lock(&_guard); // Now would be an appropriate time to set the element array buffer ids // scheduled for deconstruction to the degenerate case. while ((id = _deconstructTriElementIndex.remove()) != 0) { deconstructTriElement(id); _availableTriElementIndex.add(id); } while ((id = _constructPrimitiveIndex.remove()) != 0) { Primitive* primitive = _primitives[id]; if (primitive) { constructElements(primitive); // No need to keep an extra copy of the vertices _cpuMemoryUsage -= primitive->getMemoryUsage(); primitive->releaseVertexElements(); _cpuMemoryUsage += primitive->getMemoryUsage(); } } // The application uses clockwise winding for the definition of front face, this renderer // aalso uses clockwise (that is the gl default) to construct the triangulation // so... //glFrontFace(GL_CW); glEnable(GL_CULL_FACE); glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, sizeof(VertexElement), 0); glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer(GL_FLOAT, sizeof(VertexElement), (GLvoid const *)12); glEnableClientState(GL_COLOR_ARRAY); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexElement), (GLvoid const *)24); glBindBuffer(GL_ARRAY_BUFFER, 0); GLint err = glGetError(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _triBufferId); glDrawElements(GL_TRIANGLES, 3 * _triElementCount, GL_UNSIGNED_INT, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDisable(GL_CULL_FACE); err = glGetError(); } int PrimitiveRenderer::vGetMemoryUsage() { return _cpuMemoryUsage; } int PrimitiveRenderer::vGetMemoryUsageGPU() { return _gpuMemoryUsage; }