2333 lines · 94.8 KB · C++
RawDownload
////  VoxelSystem.cpp////  Created by Philip on 12/31/12.//  Copyright (c) 2012 High Fidelity, Inc. All rights reserved.//  #include <cstring>#include <cmath>#include <iostream> // to load voxels from file#include <fstream> // to load voxels from file #include <OctalCode.h>#include <PacketHeaders.h>#include <PerfStat.h>#include <SharedUtil.h>#include <NodeList.h> #include "Application.h"#include "InterfaceConfig.h"#include "Menu.h"#include "renderer/ProgramObject.h"#include "VoxelConstants.h"#include "VoxelSystem.h" const bool VoxelSystem::DONT_BAIL_EARLY = false; float identityVerticesGlobalNormals[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 }; float identityVertices[] = { 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, //0-7                             0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1, //8-15                             0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 }; // 16-23 GLfloat identityNormals[] = { 0,0,-1, 0,0,-1, 0,0,-1, 0,0,-1,                              0,0,+1, 0,0,+1, 0,0,+1, 0,0,+1,                              0,-1,0, 0,-1,0, 0,+1,0, 0,+1,0,                              0,-1,0, 0,-1,0, 0,+1,0, 0,+1,0,                              -1,0,0, +1,0,0, +1,0,0, -1,0,0,                              -1,0,0, +1,0,0, +1,0,0, -1,0,0 }; GLubyte identityIndices[] = { 0,2,1,    0,3,2,    // Z-                              8,9,13,   8,13,12,  // Y-                              16,23,19, 16,20,23, // X-                              17,18,22, 17,22,21, // X+                              10,11,15, 10,15,14, // Y+                              4,5,6,    4,6,7 };  // Z+ GLubyte identityIndicesTop[]    = {  2, 3, 7,  2, 7, 6 };GLubyte identityIndicesBottom[] = {  0, 1, 5,  0, 5, 4 };GLubyte identityIndicesLeft[]   = {  0, 7, 3,  0, 4, 7 };GLubyte identityIndicesRight[]  = {  1, 2, 6,  1, 6, 5 };GLubyte identityIndicesFront[]  = {  0, 2, 1,  0, 3, 2 };GLubyte identityIndicesBack[]   = {  4, 5, 6,  4, 6, 7 }; VoxelSystem::VoxelSystem(float treeScale, int maxVoxels, VoxelTree* tree)    : NodeData(),    _treeScale(treeScale),    _maxVoxels(maxVoxels),    _initialized(false),    _writeArraysLock(QReadWriteLock::Recursive),    _readArraysLock(QReadWriteLock::Recursive),    _inOcclusions(false),    _showCulledSharedFaces(false),    _usePrimitiveRenderer(false),    _renderer(0) {     _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0;    _writeRenderFullVBO = true;    _readRenderFullVBO = true;    _tree = (tree) ? tree : new VoxelTree();     _tree->getRoot()->setVoxelSystem(this);     VoxelTreeElement::addDeleteHook(this);    VoxelTreeElement::addUpdateHook(this);    _abandonedVBOSlots = 0;    _falseColorizeBySource = false;    _dataSourceUUID = QUuid();    _voxelServerCount = 0;     _viewFrustum = Application::getInstance()->getViewFrustum();     _useVoxelShader = false;    _voxelsAsPoints = false;    _voxelShaderModeWhenVoxelsAsPointsEnabled = false;     _writeVoxelShaderData = NULL;    _readVoxelShaderData = NULL;     _readVerticesArray = NULL;    _writeVerticesArray = NULL;    _readColorsArray = NULL;    _writeColorsArray = NULL;    _writeVoxelDirtyArray = NULL;    _readVoxelDirtyArray = NULL;     _inSetupNewVoxelsForDrawing = false;    _useFastVoxelPipeline = false;     _culledOnce = false;    _inhideOutOfView = false;     _lastKnownVoxelSizeScale = DEFAULT_OCTREE_SIZE_SCALE;    _lastKnownBoundaryLevelAdjust = 0;} void VoxelSystem::elementDeleted(OctreeElement* element) {    VoxelTreeElement* voxel = (VoxelTreeElement*)element;    if (voxel->getVoxelSystem() == this) {        if ((_voxelsInWriteArrays != 0) || _usePrimitiveRenderer) {            forceRemoveNodeFromArrays(voxel);        } else {            if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) {                printf("VoxelSystem::elementDeleted() while _voxelsInWriteArrays==0, is that expected? \n");            }        }    }} void VoxelSystem::setDisableFastVoxelPipeline(bool disableFastVoxelPipeline) {    _useFastVoxelPipeline = !disableFastVoxelPipeline;    setupNewVoxelsForDrawing();} void VoxelSystem::elementUpdated(OctreeElement* element) {    VoxelTreeElement* voxel = (VoxelTreeElement*)element;     // If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail..    if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) {        return;    }     if (voxel->getVoxelSystem() == this) {        bool shouldRender = false; // assume we don't need to render it        // if it's colored, we might need to render it!        float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale();        int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust();        shouldRender = voxel->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust);         if (voxel->getShouldRender() != shouldRender) {            voxel->setShouldRender(shouldRender);        }         if (!voxel->isLeaf()) {             // As we check our children, see if any of them went from shouldRender to NOT shouldRender            // then we probably dropped LOD and if we don't have color, we want to average our children            // for a new color.            int childrenGotHiddenCount = 0;            for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {                VoxelTreeElement* childVoxel = voxel->getChildAtIndex(i);                if (childVoxel) {                    bool wasShouldRender = childVoxel->getShouldRender();                    bool isShouldRender = childVoxel->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust);                    if (wasShouldRender && !isShouldRender) {                        childrenGotHiddenCount++;                    }                }            }            if (childrenGotHiddenCount > 0) {                voxel->calculateAverageFromChildren();            }        }         const bool REUSE_INDEX = true;        const bool DONT_FORCE_REDRAW = false;        updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW);        _voxelsUpdated++;         voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.         setupNewVoxelsForDrawingSingleNode();    }} // returns an available index, starts by reusing a previously freed index, but if there isn't one available// it will use the end of the VBO array and grow our accounting of that array.// and makes the index available for some other node to useglBufferIndex VoxelSystem::getNextBufferIndex() {    glBufferIndex output = GLBUFFER_INDEX_UNKNOWN;    // if there's a free index, use it...    if (_freeIndexes.size() > 0) {        _freeIndexLock.lock();        output = _freeIndexes.back();        _freeIndexes.pop_back();        _freeIndexLock.unlock();    } else {        output = _voxelsInWriteArrays;        _voxelsInWriteArrays++;    }    return output;} // Release responsibility of the buffer/vbo index from the VoxelTreeElement, and makes the index available for some other node to use// will also "clean up" the index data for the buffer/vbo slot, so that if it's in the middle of the draw range, the triangles// will be "invisible"void VoxelSystem::freeBufferIndex(glBufferIndex index) {    if (_voxelsInWriteArrays == 0) {        qDebug() << "freeBufferIndex() called when _voxelsInWriteArrays == 0!";    }     // make the index available for next node that needs to be drawn    _freeIndexLock.lock();    _freeIndexes.push_back(index);    _freeIndexLock.unlock();     // make the VBO slot "invisible" in case this slot is not used    const glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX);    const float voxelScale = 0;    const nodeColor BLACK = {0, 0, 0, 0};    updateArraysDetails(index, startVertex, voxelScale, BLACK);} // This will run through the list of _freeIndexes and reset their VBO array values to be "invisible".void VoxelSystem::clearFreeBufferIndexes() {    bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);    PerformanceWarning warn(showWarnings, "clearFreeBufferIndexes()");    _voxelsInWriteArrays = 0; // reset our VBO    _abandonedVBOSlots = 0;     // clear out freeIndexes    {        PerformanceWarning warn(showWarnings,"clearFreeBufferIndexes() : _freeIndexLock.lock()");        _freeIndexLock.lock();    }    {        PerformanceWarning warn(showWarnings,"clearFreeBufferIndexes() : _freeIndexes.clear()");        _freeIndexes.clear();    }    _freeIndexLock.unlock();} VoxelSystem::~VoxelSystem() {    VoxelTreeElement::removeDeleteHook(this);    VoxelTreeElement::removeUpdateHook(this);     cleanupVoxelMemory();    delete _tree;}  // This is called by the main application thread on both the initialization of the application and when// the preferences dialog box is called/savedvoid VoxelSystem::setMaxVoxels(int maxVoxels) {    if (maxVoxels == _maxVoxels) {        return;    }    bool wasInitialized = _initialized;    if (wasInitialized) {        clearAllNodesBufferIndex();        cleanupVoxelMemory();    }    _maxVoxels = maxVoxels;    if (wasInitialized) {        initVoxelMemory();    }    if (wasInitialized) {        forceRedrawEntireTree();    }} // This is called by the main application thread on both the initialization of the application and when// the use voxel shader menu item is chosenvoid VoxelSystem::setUseVoxelShader(bool useVoxelShader) {    if (_useVoxelShader == useVoxelShader) {        return;    }     bool wasInitialized = _initialized;    if (wasInitialized) {        clearAllNodesBufferIndex();        cleanupVoxelMemory();    }    _useVoxelShader = useVoxelShader;    _usePrimitiveRenderer = false;    if (wasInitialized) {        initVoxelMemory();    }     if (wasInitialized) {        forceRedrawEntireTree();    }} void VoxelSystem::setVoxelsAsPoints(bool voxelsAsPoints) {    if (_voxelsAsPoints == voxelsAsPoints) {        return;    }     bool wasInitialized = _initialized;     // If we're "turning on" Voxels as points, we need to double check that we're in voxel shader mode.    // Voxels as points uses the VoxelShader memory model, so if we're not in voxel shader mode,    // then set it to voxel shader mode.    if (voxelsAsPoints) {        Menu::getInstance()->getUseVoxelShader()->setEnabled(false);         // If enabling this... then do it before checking voxel shader status, that way, if voxel        // shader is already enabled, we just start drawing as points.        _voxelsAsPoints = true;         if (!_useVoxelShader) {            setUseVoxelShader(true);            _voxelShaderModeWhenVoxelsAsPointsEnabled = false;        } else {            _voxelShaderModeWhenVoxelsAsPointsEnabled = true;        }    } else {        Menu::getInstance()->getUseVoxelShader()->setEnabled(true);        // if we're turning OFF voxels as point mode, then we check what the state of voxel shader was when we enabled        // voxels as points, if it was OFF, then we return it to that value.        if (_voxelShaderModeWhenVoxelsAsPointsEnabled == false) {            setUseVoxelShader(false);        }        // If disabling this... then do it AFTER checking previous voxel shader status, that way, if voxel        // shader is was not enabled, we switch back to normal mode before turning off points.        _voxelsAsPoints = false;    }     // Set our voxels as points    if (wasInitialized) {        forceRedrawEntireTree();    }} void VoxelSystem::cleanupVoxelMemory() {    if (_initialized) {        _readArraysLock.lockForWrite();        _initialized = false; // no longer initialized        if (_useVoxelShader) {            // these are used when in VoxelShader mode.            glDeleteBuffers(1, &_vboVoxelsID);            glDeleteBuffers(1, &_vboVoxelsIndicesID);             delete[] _writeVoxelShaderData;            delete[] _readVoxelShaderData;             _writeVoxelShaderData = _readVoxelShaderData = NULL;         } else {            // Destroy  glBuffers            glDeleteBuffers(1, &_vboVerticesID);            glDeleteBuffers(1, &_vboColorsID);             glDeleteBuffers(1, &_vboIndicesTop);            glDeleteBuffers(1, &_vboIndicesBottom);            glDeleteBuffers(1, &_vboIndicesLeft);            glDeleteBuffers(1, &_vboIndicesRight);            glDeleteBuffers(1, &_vboIndicesFront);            glDeleteBuffers(1, &_vboIndicesBack);             delete[] _readVerticesArray;            delete[] _writeVerticesArray;            delete[] _readColorsArray;            delete[] _writeColorsArray;             _readVerticesArray = NULL;            _writeVerticesArray = NULL;            _readColorsArray = NULL;            _writeColorsArray = NULL;        }         delete _renderer;        _renderer = 0;         delete[] _writeVoxelDirtyArray;        delete[] _readVoxelDirtyArray;        _writeVoxelDirtyArray = _readVoxelDirtyArray = NULL;        _readArraysLock.unlock();    }} void VoxelSystem::setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndices[]) {    GLuint* indicesArray = new GLuint[INDICES_PER_FACE * _maxVoxels];     // populate the indicesArray    // this will not change given new voxels, so we can set it all up now    for (unsigned long n = 0; n < _maxVoxels; n++) {        // fill the indices array        int voxelIndexOffset = n * INDICES_PER_FACE;        GLuint* currentIndicesPos = indicesArray + voxelIndexOffset;        int startIndex = (n * GLOBAL_NORMALS_VERTICES_PER_VOXEL);         for (int i = 0; i < INDICES_PER_FACE; i++) {            // add indices for this side of the cube            currentIndicesPos[i] = startIndex + faceIdentityIndices[i];        }    }     glGenBuffers(1, &faceVBOID);    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, faceVBOID);    glBufferData(GL_ELEMENT_ARRAY_BUFFER,                 INDICES_PER_FACE * sizeof(GLuint) * _maxVoxels,                 indicesArray, GL_STATIC_DRAW);    _memoryUsageVBO += INDICES_PER_FACE * sizeof(GLuint) * _maxVoxels;     // delete the indices and normals arrays that are no longer needed    delete[] indicesArray;} void VoxelSystem::initVoxelMemory() {    _readArraysLock.lockForWrite();    _writeArraysLock.lockForWrite();     _memoryUsageRAM = 0;    _memoryUsageVBO = 0; // our VBO allocations as we know them     // if _voxelsAsPoints then we must have _useVoxelShader    if (_voxelsAsPoints && !_useVoxelShader) {        _useVoxelShader = true;    }     if (_useVoxelShader) {        GLuint* indicesArray = new GLuint[_maxVoxels];         // populate the indicesArray        // this will not change given new voxels, so we can set it all up now        for (unsigned long n = 0; n < _maxVoxels; n++) {            indicesArray[n] = n;        }         // bind the indices VBO to the actual indices array        glGenBuffers(1, &_vboVoxelsIndicesID);        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboVoxelsIndicesID);        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * _maxVoxels, indicesArray, GL_STATIC_DRAW);        _memoryUsageVBO += sizeof(GLuint) * _maxVoxels;         glGenBuffers(1, &_vboVoxelsID);        glBindBuffer(GL_ARRAY_BUFFER, _vboVoxelsID);        glBufferData(GL_ARRAY_BUFFER, _maxVoxels * sizeof(VoxelShaderVBOData), NULL, GL_DYNAMIC_DRAW);        _memoryUsageVBO += _maxVoxels * sizeof(VoxelShaderVBOData);         // delete the indices and normals arrays that are no longer needed        delete[] indicesArray;         // we will track individual dirty sections with these arrays of bools        _writeVoxelDirtyArray = new bool[_maxVoxels];        memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool));        _memoryUsageRAM += (_maxVoxels * sizeof(bool));         _readVoxelDirtyArray = new bool[_maxVoxels];        memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool));        _memoryUsageRAM += (_maxVoxels * sizeof(bool));         // prep the data structures for incoming voxel data        _writeVoxelShaderData = new VoxelShaderVBOData[_maxVoxels];        _memoryUsageRAM += (sizeof(VoxelShaderVBOData) * _maxVoxels);         _readVoxelShaderData = new VoxelShaderVBOData[_maxVoxels];        _memoryUsageRAM += (sizeof(VoxelShaderVBOData) * _maxVoxels);    } else {         // Global Normals mode uses a technique of not including normals on any voxel vertices, and instead        // rendering the voxel faces in 6 passes that use a global call to glNormal3f()        setupFaceIndices(_vboIndicesTop,    identityIndicesTop);        setupFaceIndices(_vboIndicesBottom, identityIndicesBottom);        setupFaceIndices(_vboIndicesLeft,   identityIndicesLeft);        setupFaceIndices(_vboIndicesRight,  identityIndicesRight);        setupFaceIndices(_vboIndicesFront,  identityIndicesFront);        setupFaceIndices(_vboIndicesBack,   identityIndicesBack);         // Depending on if we're using per vertex normals, we will need more or less vertex points per voxel        int vertexPointsPerVoxel = GLOBAL_NORMALS_VERTEX_POINTS_PER_VOXEL;        glGenBuffers(1, &_vboVerticesID);        glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);        glBufferData(GL_ARRAY_BUFFER, vertexPointsPerVoxel * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);        _memoryUsageVBO += vertexPointsPerVoxel * sizeof(GLfloat) * _maxVoxels;         // VBO for colorsArray        glGenBuffers(1, &_vboColorsID);        glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);        glBufferData(GL_ARRAY_BUFFER, vertexPointsPerVoxel * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW);        _memoryUsageVBO += vertexPointsPerVoxel * sizeof(GLubyte) * _maxVoxels;         // we will track individual dirty sections with these arrays of bools        _writeVoxelDirtyArray = new bool[_maxVoxels];        memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool));        _memoryUsageRAM += (sizeof(bool) * _maxVoxels);         _readVoxelDirtyArray = new bool[_maxVoxels];        memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool));        _memoryUsageRAM += (sizeof(bool) * _maxVoxels);         // prep the data structures for incoming voxel data        _writeVerticesArray = new GLfloat[vertexPointsPerVoxel * _maxVoxels];        _memoryUsageRAM += (sizeof(GLfloat) * vertexPointsPerVoxel * _maxVoxels);        _readVerticesArray = new GLfloat[vertexPointsPerVoxel * _maxVoxels];        _memoryUsageRAM += (sizeof(GLfloat) * vertexPointsPerVoxel * _maxVoxels);         _writeColorsArray = new GLubyte[vertexPointsPerVoxel * _maxVoxels];        _memoryUsageRAM += (sizeof(GLubyte) * vertexPointsPerVoxel * _maxVoxels);        _readColorsArray = new GLubyte[vertexPointsPerVoxel * _maxVoxels];        _memoryUsageRAM += (sizeof(GLubyte) * vertexPointsPerVoxel * _maxVoxels);         // create our simple fragment shader if we're the first system to init        if (!_perlinModulateProgram.isLinked()) {            _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath()                                                           + "shaders/perlin_modulate.vert");

Lines 1–500 of 2333.