//// Cube.cpp// interface//// Created by Philip on 12/31/12.// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.// #ifdef _WIN32#define _timeval_#define _USE_MATH_DEFINES#endif #include <cstring>#include <cmath>#include <iostream> // to load voxels from file#include <fstream> // to load voxels from file#include <pthread.h> #include <OctalCode.h>#include <PacketHeaders.h>#include <PerfStat.h>#include <SharedUtil.h>#include <NodeList.h>#include <NodeTypes.h> #include "Application.h"#include "CoverageMap.h"#include "CoverageMapV2.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) : NodeData(NULL), _treeScale(treeScale), _maxVoxels(maxVoxels), _initialized(false) { _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0; _writeRenderFullVBO = true; _readRenderFullVBO = true; _tree = new VoxelTree(); _tree->rootNode->setVoxelSystem(this); pthread_mutex_init(&_bufferWriteLock, NULL); pthread_mutex_init(&_treeLock, NULL); VoxelNode::addDeleteHook(this); VoxelNode::addUpdateHook(this); _abandonedVBOSlots = 0; _falseColorizeBySource = false; _dataSourceID = UNKNOWN_NODE_ID; _voxelServerCount = 0; _viewFrustum = Application::getInstance()->getViewFrustum(); connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); _useVoxelShader = false; _writeVoxelShaderData = NULL; _readVoxelShaderData = NULL; _readVerticesArray = NULL; _writeVerticesArray = NULL; _readColorsArray = NULL; _writeColorsArray = NULL; _writeVoxelDirtyArray = NULL; _readVoxelDirtyArray = NULL; _inSetupNewVoxelsForDrawing = false; _useFastVoxelPipeline = false;} void VoxelSystem::voxelDeleted(VoxelNode* node) { if (node->isKnownBufferIndex() && (node->getVoxelSystem() == this)) { if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) { freeBufferIndex(node->getBufferIndex()); } else { forceRemoveNodeFromArraysAsPartialVBO(node); freeBufferIndex(node->getBufferIndex()); } }} void VoxelSystem::setUseFastVoxelPipeline(bool useFastVoxelPipeline) { _useFastVoxelPipeline = useFastVoxelPipeline; printf("setUseFastVoxelPipeline() _useFastVoxelPipeline=%s\n", debug::valueOf(_useFastVoxelPipeline)); setupNewVoxelsForDrawing();} void VoxelSystem::voxelUpdated(VoxelNode* node) { // If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail.. if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) { return; } if (node->getVoxelSystem() == this) { bool shouldRender = false; // assume we don't need to render it // if it's colored, we might need to render it! shouldRender = node->calculateShouldRender(_viewFrustum); if (node->getShouldRender() != shouldRender) { node->setShouldRender(shouldRender); } if (!node->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++) { VoxelNode* childNode = node->getChildAtIndex(i); if (childNode) { bool wasShouldRender = childNode->getShouldRender(); bool isShouldRender = childNode->calculateShouldRender(_viewFrustum); if (wasShouldRender && !isShouldRender) { childrenGotHiddenCount++; } } } if (childrenGotHiddenCount > 0) { node->setColorFromAverageOfChildren(); } } updateNodeInArraysAsPartialVBO(node); _voxelsUpdated++; node->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) { output = _freeIndexes.back(); _freeIndexes.pop_back(); } else { output = _voxelsInWriteArrays; _voxelsInWriteArrays++; } return output;} // Doesn't actually clean up the VBOs for the index, but does release responsibility of the index from the VoxelNode, // and makes the index available for some other node to usevoid VoxelSystem::freeBufferIndex(glBufferIndex index) { _freeIndexes.push_back(index);} // This will run through the list of _freeIndexes and reset their VBO array values to be "invisible".void VoxelSystem::clearFreeBufferIndexes() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "###### clearFreeBufferIndexes()"); for (int i = 0; i < _freeIndexes.size(); i++) { glBufferIndex nodeIndex = _freeIndexes[i]; glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX); float voxelScale = 0; _writeVoxelDirtyArray[nodeIndex] = true; nodeColor color = {0, 0, 0, 0}; updateNodeInArrays(nodeIndex, startVertex, voxelScale, color); _abandonedVBOSlots++; } _freeIndexes.clear();} VoxelSystem::~VoxelSystem() { cleanupVoxelMemory(); delete _tree; pthread_mutex_destroy(&_bufferWriteLock); pthread_mutex_destroy(&_treeLock); VoxelNode::removeDeleteHook(this); VoxelNode::removeUpdateHook(this);} void VoxelSystem::setMaxVoxels(int maxVoxels) { if (maxVoxels == _maxVoxels) { return; } pthread_mutex_lock(&_bufferWriteLock); bool wasInitialized = _initialized; if (wasInitialized) { clearAllNodesBufferIndex(); cleanupVoxelMemory(); } _maxVoxels = maxVoxels; if (wasInitialized) { init(); } pthread_mutex_unlock(&_bufferWriteLock); if (wasInitialized) { forceRedrawEntireTree(); }} void VoxelSystem::setUseVoxelShader(bool useVoxelShader) { if (_useVoxelShader == useVoxelShader) { return; } pthread_mutex_lock(&_bufferWriteLock); bool wasInitialized = _initialized; if (wasInitialized) { clearAllNodesBufferIndex(); cleanupVoxelMemory(); } _useVoxelShader = useVoxelShader; if (wasInitialized) { init(); } pthread_mutex_unlock(&_bufferWriteLock); if (wasInitialized) { forceRedrawEntireTree(); }} void VoxelSystem::cleanupVoxelMemory() { if (_initialized) { if (_useVoxelShader) { // these are used when in VoxelShader mode. glDeleteBuffers(1, &_vboVoxelsID); glDeleteBuffers(1, &_vboVoxelsIndicesID); delete[] _writeVoxelShaderData; delete[] _readVoxelShaderData; } 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; } delete[] _writeVoxelDirtyArray; delete[] _readVoxelDirtyArray; } _initialized = false; // no longer initialized} 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 (int 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() { _initialMemoryUsageGPU = getFreeMemoryGPU(); _memoryUsageRAM = 0; _memoryUsageVBO = 0; // our VBO allocations as we know them if (_useVoxelShader) { qDebug("Using Voxel Shader...\n"); GLuint* indicesArray = new GLuint[_maxVoxels]; // populate the indicesArray // this will not change given new voxels, so we can set it all up now for (int 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() qDebug("Using Global Normals...\n"); 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()) { switchToResourcesParentIfRequired(); _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/perlin_modulate.vert"); _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/perlin_modulate.frag"); _perlinModulateProgram.link(); _perlinModulateProgram.bind(); _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); _perlinModulateProgram.release(); } }} void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { _tree->loadVoxelsFile(fileName, wantColorRandomizer); setupNewVoxelsForDrawing();} void VoxelSystem::writeToSVOFile(const char* filename, VoxelNode* node) const { _tree->writeToSVOFile(filename, node);} bool VoxelSystem::readFromSVOFile(const char* filename) { bool result = _tree->readFromSVOFile(filename); if (result) { setupNewVoxelsForDrawing(); } return result;} bool VoxelSystem::readFromSquareARGB32Pixels(const char *filename) { bool result = _tree->readFromSquareARGB32Pixels(filename); if (result) { setupNewVoxelsForDrawing(); } return result;} bool VoxelSystem::readFromSchematicFile(const char* filename) { bool result = _tree->readFromSchematicFile(filename); if (result) { setupNewVoxelsForDrawing(); } return result;} long int VoxelSystem::getVoxelsCreated() { return _tree->voxelsCreated;} float VoxelSystem::getVoxelsCreatedPerSecondAverage() { return (1 / _tree->voxelsCreatedStats.getEventDeltaAverage());} long int VoxelSystem::getVoxelsColored() { return _tree->voxelsColored;} float VoxelSystem::getVoxelsColoredPerSecondAverage() { return (1 / _tree->voxelsColoredStats.getEventDeltaAverage());} long int VoxelSystem::getVoxelsBytesRead() { return _tree->voxelsBytesRead;} float VoxelSystem::getVoxelsBytesReadPerSecondAverage() { return _tree->voxelsBytesReadStats.getAverageSampleValuePerSecond();} int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { unsigned char command = *sourceBuffer; int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer); unsigned char* voxelData = sourceBuffer + numBytesPacketHeader; pthread_mutex_lock(&_treeLock); switch(command) { case PACKET_TYPE_VOXEL_DATA: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "readBitstreamToTree()"); // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(WANT_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceID()); _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args); } break; case PACKET_TYPE_VOXEL_DATA_MONOCHROME: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "readBitstreamToTree()"); // ask the VoxelTree to read the MONOCHROME bitstream into the treeLines 1–500 of 2142. Load more
// // Cube.cpp // interface // // Created by Philip on 12/31/12. // Copyright (c) 2012 High Fidelity, Inc. All rights reserved. // #ifdef _WIN32 #define _timeval_ #define _USE_MATH_DEFINES #endif #include <cstring> #include <cmath> #include <iostream> // to load voxels from file #include <fstream> // to load voxels from file #include <pthread.h> #include <OctalCode.h> #include <PacketHeaders.h> #include <PerfStat.h> #include <SharedUtil.h> #include <NodeList.h> #include <NodeTypes.h> #include "Application.h" #include "CoverageMap.h" #include "CoverageMapV2.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) : NodeData(NULL), _treeScale(treeScale), _maxVoxels(maxVoxels), _initialized(false) { _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0; _writeRenderFullVBO = true; _readRenderFullVBO = true; _tree = new VoxelTree(); _tree->rootNode->setVoxelSystem(this); pthread_mutex_init(&_bufferWriteLock, NULL); pthread_mutex_init(&_treeLock, NULL); VoxelNode::addDeleteHook(this); VoxelNode::addUpdateHook(this); _abandonedVBOSlots = 0; _falseColorizeBySource = false; _dataSourceID = UNKNOWN_NODE_ID; _voxelServerCount = 0; _viewFrustum = Application::getInstance()->getViewFrustum(); connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); _useVoxelShader = false; _writeVoxelShaderData = NULL; _readVoxelShaderData = NULL; _readVerticesArray = NULL; _writeVerticesArray = NULL; _readColorsArray = NULL; _writeColorsArray = NULL; _writeVoxelDirtyArray = NULL; _readVoxelDirtyArray = NULL; _inSetupNewVoxelsForDrawing = false; _useFastVoxelPipeline = false; } void VoxelSystem::voxelDeleted(VoxelNode* node) { if (node->isKnownBufferIndex() && (node->getVoxelSystem() == this)) { if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) { freeBufferIndex(node->getBufferIndex()); } else { forceRemoveNodeFromArraysAsPartialVBO(node); freeBufferIndex(node->getBufferIndex()); } } } void VoxelSystem::setUseFastVoxelPipeline(bool useFastVoxelPipeline) { _useFastVoxelPipeline = useFastVoxelPipeline; printf("setUseFastVoxelPipeline() _useFastVoxelPipeline=%s\n", debug::valueOf(_useFastVoxelPipeline)); setupNewVoxelsForDrawing(); } void VoxelSystem::voxelUpdated(VoxelNode* node) { // If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail.. if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) { return; } if (node->getVoxelSystem() == this) { bool shouldRender = false; // assume we don't need to render it // if it's colored, we might need to render it! shouldRender = node->calculateShouldRender(_viewFrustum); if (node->getShouldRender() != shouldRender) { node->setShouldRender(shouldRender); } if (!node->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++) { VoxelNode* childNode = node->getChildAtIndex(i); if (childNode) { bool wasShouldRender = childNode->getShouldRender(); bool isShouldRender = childNode->calculateShouldRender(_viewFrustum); if (wasShouldRender && !isShouldRender) { childrenGotHiddenCount++; } } } if (childrenGotHiddenCount > 0) { node->setColorFromAverageOfChildren(); } } updateNodeInArraysAsPartialVBO(node); _voxelsUpdated++; node->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) { output = _freeIndexes.back(); _freeIndexes.pop_back(); } else { output = _voxelsInWriteArrays; _voxelsInWriteArrays++; } return output; } // Doesn't actually clean up the VBOs for the index, but does release responsibility of the index from the VoxelNode, // and makes the index available for some other node to use void VoxelSystem::freeBufferIndex(glBufferIndex index) { _freeIndexes.push_back(index); } // This will run through the list of _freeIndexes and reset their VBO array values to be "invisible". void VoxelSystem::clearFreeBufferIndexes() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "###### clearFreeBufferIndexes()"); for (int i = 0; i < _freeIndexes.size(); i++) { glBufferIndex nodeIndex = _freeIndexes[i]; glm::vec3 startVertex(FLT_MAX, FLT_MAX, FLT_MAX); float voxelScale = 0; _writeVoxelDirtyArray[nodeIndex] = true; nodeColor color = {0, 0, 0, 0}; updateNodeInArrays(nodeIndex, startVertex, voxelScale, color); _abandonedVBOSlots++; } _freeIndexes.clear(); } VoxelSystem::~VoxelSystem() { cleanupVoxelMemory(); delete _tree; pthread_mutex_destroy(&_bufferWriteLock); pthread_mutex_destroy(&_treeLock); VoxelNode::removeDeleteHook(this); VoxelNode::removeUpdateHook(this); } void VoxelSystem::setMaxVoxels(int maxVoxels) { if (maxVoxels == _maxVoxels) { return; } pthread_mutex_lock(&_bufferWriteLock); bool wasInitialized = _initialized; if (wasInitialized) { clearAllNodesBufferIndex(); cleanupVoxelMemory(); } _maxVoxels = maxVoxels; if (wasInitialized) { init(); } pthread_mutex_unlock(&_bufferWriteLock); if (wasInitialized) { forceRedrawEntireTree(); } } void VoxelSystem::setUseVoxelShader(bool useVoxelShader) { if (_useVoxelShader == useVoxelShader) { return; } pthread_mutex_lock(&_bufferWriteLock); bool wasInitialized = _initialized; if (wasInitialized) { clearAllNodesBufferIndex(); cleanupVoxelMemory(); } _useVoxelShader = useVoxelShader; if (wasInitialized) { init(); } pthread_mutex_unlock(&_bufferWriteLock); if (wasInitialized) { forceRedrawEntireTree(); } } void VoxelSystem::cleanupVoxelMemory() { if (_initialized) { if (_useVoxelShader) { // these are used when in VoxelShader mode. glDeleteBuffers(1, &_vboVoxelsID); glDeleteBuffers(1, &_vboVoxelsIndicesID); delete[] _writeVoxelShaderData; delete[] _readVoxelShaderData; } 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; } delete[] _writeVoxelDirtyArray; delete[] _readVoxelDirtyArray; } _initialized = false; // no longer initialized } 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 (int 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() { _initialMemoryUsageGPU = getFreeMemoryGPU(); _memoryUsageRAM = 0; _memoryUsageVBO = 0; // our VBO allocations as we know them if (_useVoxelShader) { qDebug("Using Voxel Shader...\n"); GLuint* indicesArray = new GLuint[_maxVoxels]; // populate the indicesArray // this will not change given new voxels, so we can set it all up now for (int 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() qDebug("Using Global Normals...\n"); 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()) { switchToResourcesParentIfRequired(); _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/perlin_modulate.vert"); _perlinModulateProgram.addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/perlin_modulate.frag"); _perlinModulateProgram.link(); _perlinModulateProgram.bind(); _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); _perlinModulateProgram.release(); } } } void VoxelSystem::loadVoxelsFile(const char* fileName, bool wantColorRandomizer) { _tree->loadVoxelsFile(fileName, wantColorRandomizer); setupNewVoxelsForDrawing(); } void VoxelSystem::writeToSVOFile(const char* filename, VoxelNode* node) const { _tree->writeToSVOFile(filename, node); } bool VoxelSystem::readFromSVOFile(const char* filename) { bool result = _tree->readFromSVOFile(filename); if (result) { setupNewVoxelsForDrawing(); } return result; } bool VoxelSystem::readFromSquareARGB32Pixels(const char *filename) { bool result = _tree->readFromSquareARGB32Pixels(filename); if (result) { setupNewVoxelsForDrawing(); } return result; } bool VoxelSystem::readFromSchematicFile(const char* filename) { bool result = _tree->readFromSchematicFile(filename); if (result) { setupNewVoxelsForDrawing(); } return result; } long int VoxelSystem::getVoxelsCreated() { return _tree->voxelsCreated; } float VoxelSystem::getVoxelsCreatedPerSecondAverage() { return (1 / _tree->voxelsCreatedStats.getEventDeltaAverage()); } long int VoxelSystem::getVoxelsColored() { return _tree->voxelsColored; } float VoxelSystem::getVoxelsColoredPerSecondAverage() { return (1 / _tree->voxelsColoredStats.getEventDeltaAverage()); } long int VoxelSystem::getVoxelsBytesRead() { return _tree->voxelsBytesRead; } float VoxelSystem::getVoxelsBytesReadPerSecondAverage() { return _tree->voxelsBytesReadStats.getAverageSampleValuePerSecond(); } int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { unsigned char command = *sourceBuffer; int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer); unsigned char* voxelData = sourceBuffer + numBytesPacketHeader; pthread_mutex_lock(&_treeLock); switch(command) { case PACKET_TYPE_VOXEL_DATA: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "readBitstreamToTree()"); // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(WANT_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceID()); _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args); } break; case PACKET_TYPE_VOXEL_DATA_MONOCHROME: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "readBitstreamToTree()"); // ask the VoxelTree to read the MONOCHROME bitstream into the tree