//// 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. Load more
// // 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 use glBufferIndex 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/saved void 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 chosen void 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");