_setupNewVoxelsForDrawingLastElapsed = 0; _lastViewCullingElapsed = _lastViewCulling = 0; // When we change voxels representations in the arrays, we'll update this _voxelsDirty = false; _voxelsInWriteArrays = 0; _voxelsInReadArrays = 0; // we will track individual dirty sections with these arrays of bools _writeVoxelDirtyArray = new bool[_maxVoxels]; memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); _readVoxelDirtyArray = new bool[_maxVoxels]; memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); // prep the data structures for incoming voxel data _writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; _readVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; _writeColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; _readColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; GLuint* indicesArray = new GLuint[INDICES_PER_VOXEL * _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_VOXEL; GLuint* currentIndicesPos = indicesArray + voxelIndexOffset; int startIndex = (n * VERTICES_PER_VOXEL); for (int i = 0; i < INDICES_PER_VOXEL; i++) { // add indices for this side of the cube currentIndicesPos[i] = startIndex + identityIndices[i]; } } GLfloat* normalsArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; GLfloat* normalsArrayEndPointer = normalsArray; // populate the normalsArray for (int n = 0; n < _maxVoxels; n++) { for (int i = 0; i < VERTEX_POINTS_PER_VOXEL; i++) { *(normalsArrayEndPointer++) = identityNormals[i]; } } // VBO for the verticesArray glGenBuffers(1, &_vboVerticesID); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW); // VBO for the normalsArray glGenBuffers(1, &_vboNormalsID); glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, normalsArray, GL_STATIC_DRAW); // VBO for colorsArray glGenBuffers(1, &_vboColorsID); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW); // VBO for the indicesArray glGenBuffers(1, &_vboIndicesID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES_PER_VOXEL * sizeof(GLuint) * _maxVoxels, indicesArray, GL_STATIC_DRAW); // delete the indices and normals arrays that are no longer needed delete[] indicesArray; delete[] normalsArray; // create our simple fragment shader if we're the first system to init if (_perlinModulateProgram != 0) { return; } switchToResourcesParentIfRequired(); _perlinModulateProgram = new ProgramObject(); _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::updateFullVBOs() { updateVBOSegment(0, _voxelsInReadArrays); // consider the _readVoxelDirtyArray[] clean! memset(_readVoxelDirtyArray, false, _voxelsInReadArrays * sizeof(bool));} void VoxelSystem::updatePartialVBOs() { glBufferIndex segmentStart = 0; bool inSegment = false; for (glBufferIndex i = 0; i < _voxelsInReadArrays; i++) { bool thisVoxelDirty = _readVoxelDirtyArray[i]; if (!inSegment) { if (thisVoxelDirty) { segmentStart = i; inSegment = true; _readVoxelDirtyArray[i] = false; // consider us clean! } } else { if (!thisVoxelDirty) { // If we got here because because this voxel is NOT dirty, so the last dirty voxel was the one before // this one and so that's where the "segment" ends updateVBOSegment(segmentStart, i - 1); inSegment = false; } _readVoxelDirtyArray[i] = false; // consider us clean! } } // if we got to the end of the array, and we're in an active dirty segment... if (inSegment) { updateVBOSegment(segmentStart, _voxelsInReadArrays - 1); inSegment = false; }} void VoxelSystem::updateVBOs() { static char buffer[40] = { 0 }; if (_renderWarningsOn) { sprintf(buffer, "updateVBOs() _readRenderFullVBO=%s", debug::valueOf(_readRenderFullVBO)); }; PerformanceWarning warn(_renderWarningsOn, buffer); // would like to include _callsToTreesToArrays if (_voxelsDirty) { if (_readRenderFullVBO) { updateFullVBOs(); } else { updatePartialVBOs(); } _voxelsDirty = false; _readRenderFullVBO = false; } _callsToTreesToArrays = 0; // clear it} void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) { int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);} void VoxelSystem::render(bool texture) { PerformanceWarning warn(_renderWarningsOn, "render()"); // get the lock so that the update thread won't change anything pthread_mutex_lock(&_bufferWriteLock); updateVBOs(); // tell OpenGL where to find vertex and color information glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glVertexPointer(3, GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); glNormalPointer(GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); applyScaleAndBindProgram(texture); // for performance, disable blending and enable backface culling glDisable(GL_BLEND); glEnable(GL_CULL_FACE); // draw the number of voxels we have glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); glDrawRangeElementsEXT(GL_TRIANGLES, 0, VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, 36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glEnable(GL_BLEND); glDisable(GL_CULL_FACE); removeScaleAndReleaseProgram(texture); // deactivate vertex and color arrays after drawing glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); // bind with 0 to switch back to normal operation glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); pthread_mutex_unlock(&_bufferWriteLock);} void VoxelSystem::applyScaleAndBindProgram(bool texture) { glPushMatrix(); glScalef(_treeScale, _treeScale, _treeScale); if (texture) { _perlinModulateProgram->bind(); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID()); }} void VoxelSystem::removeScaleAndReleaseProgram(bool texture) { // scale back down to 1 so heads aren't massive glPopMatrix(); if (texture) { _perlinModulateProgram->release(); glBindTexture(GL_TEXTURE_2D, 0); }} int VoxelSystem::_nodeCount = 0; void VoxelSystem::killLocalVoxels() { _tree->eraseAllVoxels(); _voxelsInWriteArrays = _voxelsInReadArrays = 0; // better way to do this?? //setupNewVoxelsForDrawing();} bool VoxelSystem::randomColorOperation(VoxelNode* node, void* extraData) { _nodeCount++; if (node->isColored()) { nodeColor newColor = { 255, randomColorValue(150), randomColorValue(150), 1 }; node->setColor(newColor); } return true;} void VoxelSystem::randomizeVoxelColors() { _nodeCount = 0; _tree->recurseTreeWithOperation(randomColorOperation); qDebug("setting randomized true color for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing();} bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, void* extraData) { _nodeCount++; // always false colorize node->setFalseColor(255, randomColorValue(150), randomColorValue(150)); return true; // keep going!} void VoxelSystem::falseColorizeRandom() { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeRandomOperation); qDebug("setting randomized false color for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing();} bool VoxelSystem::trueColorizeOperation(VoxelNode* node, void* extraData) { _nodeCount++; node->setFalseColored(false); return true;} void VoxelSystem::trueColorize() { PerformanceWarning warn(true, "trueColorize()",true); _nodeCount = 0; _tree->recurseTreeWithOperation(trueColorizeOperation); qDebug("setting true color for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing();} // Will false colorize voxels that are not in viewbool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, void* extraData) { const ViewFrustum* viewFrustum = (const ViewFrustum*) extraData; _nodeCount++; if (node->isColored()) { if (!node->isInView(*viewFrustum)) { // Out of view voxels are colored RED node->setFalseColor(255, 0, 0); } } return true; // keep going!} void VoxelSystem::falseColorizeInView(ViewFrustum* viewFrustum) { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeInViewOperation,(void*)viewFrustum); qDebug("setting in view false color for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing();} // helper classes and args for falseColorizeBySourceclass groupColor {public: unsigned char red, green, blue; groupColor(unsigned char red, unsigned char green, unsigned char blue) : red(red), green(green), blue(blue) { }; groupColor() : red(0), green(0), blue(0) { };}; class colorizeBySourceArgs {public: std::map<uint16_t, groupColor> colors;}; // Will false colorize voxels that are not in viewbool VoxelSystem::falseColorizeBySourceOperation(VoxelNode* node, void* extraData) { colorizeBySourceArgs* args = (colorizeBySourceArgs*)extraData; _nodeCount++; if (node->isColored()) { // pick a color based on the source - we want each source to be obviously different uint16_t nodeID = node->getSourceID(); //printf("false colorizing from source %d, color: %d, %d, %d\n", nodeID, // args->colors[nodeID].red, args->colors[nodeID].green, args->colors[nodeID].blue); node->setFalseColor(args->colors[nodeID].red, args->colors[nodeID].green, args->colors[nodeID].blue); } return true; // keep going!} void VoxelSystem::falseColorizeBySource() { _nodeCount = 0; colorizeBySourceArgs args; const int NUMBER_OF_COLOR_GROUPS = 6; const unsigned char MIN_COLOR = 128; int voxelServerCount = 0; groupColor groupColors[NUMBER_OF_COLOR_GROUPS] = { groupColor(255, 0, 0), groupColor( 0, 255, 0), groupColor( 0, 0, 255), groupColor(255, 0, 255), groupColor( 0, 255, 255), groupColor(255, 255, 255) }; // create a bunch of colors we'll use during colorization NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) { uint16_t nodeID = node->getNodeID(); int groupColor = voxelServerCount % NUMBER_OF_COLOR_GROUPS; args.colors[nodeID] = groupColors[groupColor]; //printf("assigning color for source %d, color: %d, %d, %d\n", nodeID, // args.colors[nodeID].red, args.colors[nodeID].green, args.colors[nodeID].blue); if (groupColors[groupColor].red > 0) { groupColors[groupColor].red = ((groupColors[groupColor].red - MIN_COLOR)/2) + MIN_COLOR; } if (groupColors[groupColor].green > 0) { groupColors[groupColor].green = ((groupColors[groupColor].green - MIN_COLOR)/2) + MIN_COLOR; } if (groupColors[groupColor].blue > 0) { groupColors[groupColor].blue = ((groupColors[groupColor].blue - MIN_COLOR)/2) + MIN_COLOR; } voxelServerCount++; } } _tree->recurseTreeWithOperation(falseColorizeBySourceOperation, &args); qDebug("setting false color by source for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing();} // Will false colorize voxels based on distance from viewbool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; if (node->isColored()) { float distance = node->distanceToCamera(*viewFrustum); _nodeCount++; float distanceRatio = (_minDistance == _maxDistance) ? 1 : (distance - _minDistance) / (_maxDistance - _minDistance); // We want to colorize this in 16 bug chunks of color const unsigned char maxColor = 255; const unsigned char colorBands = 16; const unsigned char gradientOver = 128; unsigned char colorBand = (colorBands * distanceRatio); node->setFalseColor((colorBand * (gradientOver / colorBands)) + (maxColor - gradientOver), 0, 0); } return true; // keep going!} float VoxelSystem::_maxDistance = 0.0;float VoxelSystem::_minDistance = FLT_MAX; // Helper function will get the distance from view range, would be nice if you could just keep track// of this as voxels are created and/or colored... seems like some transform math could do that so// we wouldn't need to do two passes of the treebool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; // only do this for truly colored voxels... if (node->isColored()) { float distance = node->distanceToCamera(*viewFrustum); // calculate the range of distances if (distance > _maxDistance) { _maxDistance = distance; } if (distance < _minDistance) { _minDistance = distance; } _nodeCount++; } return true; // keep going!} void VoxelSystem::falseColorizeDistanceFromView(ViewFrustum* viewFrustum) { _nodeCount = 0; _maxDistance = 0.0; _minDistance = FLT_MAX; _tree->recurseTreeWithOperation(getDistanceFromViewRangeOperation,(void*)viewFrustum); qDebug("determining distance range for %d nodes\n", _nodeCount); _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeDistanceFromViewOperation,(void*)viewFrustum); qDebug("setting in distance false color for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing();} // combines the removeOutOfView args into a single classclass removeOutOfViewArgs {public: VoxelSystem* thisVoxelSystem; VoxelNodeBag dontRecurseBag; unsigned long nodesScanned; unsigned long nodesRemoved; unsigned long nodesInside; unsigned long nodesIntersect; unsigned long nodesOutside; removeOutOfViewArgs(VoxelSystem* voxelSystem) : thisVoxelSystem(voxelSystem), dontRecurseBag(), nodesScanned(0), nodesRemoved(0), nodesInside(0), nodesIntersect(0), nodesOutside(0) { }}; // "Remove" voxels from the tree that are not in view. We don't actually delete them,// we remove them from the tree and place them into a holding area for later deletionbool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) { removeOutOfViewArgs* args = (removeOutOfViewArgs*)extraData; // If our node was previously added to the don't recurse bag, then return false to // stop the further recursion. This means that the whole node and it's children are // known to be in view, so don't recurse them if (args->dontRecurseBag.contains(node)) { args->dontRecurseBag.remove(node); return false; // stop recursion } VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; args->nodesScanned++; // Need to operate on our child nodes, so we can remove them for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { VoxelNode* childNode = node->getChildAtIndex(i); if (childNode) { ViewFrustum::location inFrustum = childNode->inFrustum(*Application::getInstance()->getViewFrustum()); switch (inFrustum) { case ViewFrustum::OUTSIDE: { args->nodesOutside++; args->nodesRemoved++; node->removeChildAtIndex(i); thisVoxelSystem->_removedVoxels.insert(childNode); // by removing the child, it will not get recursed! } break; case ViewFrustum::INSIDE: { // if the child node is fully INSIDE the view, then there's no need to recurse it // because we know all it's children will also be in the view, so we want to // tell the caller to NOT recurse this child args->nodesInside++; args->dontRecurseBag.insert(childNode); } break; case ViewFrustum::INTERSECT: { // if the child node INTERSECTs the view, then we don't want to remove it because // it is at least partially in view. But we DO want to recurse the children because // some of them may not be in view... nothing specifically to do, just keep iterating // the children args->nodesIntersect++;Lines 501–1000 of 1579. Previous lines Load more
_setupNewVoxelsForDrawingLastElapsed = 0; _lastViewCullingElapsed = _lastViewCulling = 0; // When we change voxels representations in the arrays, we'll update this _voxelsDirty = false; _voxelsInWriteArrays = 0; _voxelsInReadArrays = 0; // we will track individual dirty sections with these arrays of bools _writeVoxelDirtyArray = new bool[_maxVoxels]; memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); _readVoxelDirtyArray = new bool[_maxVoxels]; memset(_readVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); // prep the data structures for incoming voxel data _writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; _readVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; _writeColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; _readColorsArray = new GLubyte[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; GLuint* indicesArray = new GLuint[INDICES_PER_VOXEL * _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_VOXEL; GLuint* currentIndicesPos = indicesArray + voxelIndexOffset; int startIndex = (n * VERTICES_PER_VOXEL); for (int i = 0; i < INDICES_PER_VOXEL; i++) { // add indices for this side of the cube currentIndicesPos[i] = startIndex + identityIndices[i]; } } GLfloat* normalsArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * _maxVoxels]; GLfloat* normalsArrayEndPointer = normalsArray; // populate the normalsArray for (int n = 0; n < _maxVoxels; n++) { for (int i = 0; i < VERTEX_POINTS_PER_VOXEL; i++) { *(normalsArrayEndPointer++) = identityNormals[i]; } } // VBO for the verticesArray glGenBuffers(1, &_vboVerticesID); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, NULL, GL_DYNAMIC_DRAW); // VBO for the normalsArray glGenBuffers(1, &_vboNormalsID); glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat) * _maxVoxels, normalsArray, GL_STATIC_DRAW); // VBO for colorsArray glGenBuffers(1, &_vboColorsID); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferData(GL_ARRAY_BUFFER, VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte) * _maxVoxels, NULL, GL_DYNAMIC_DRAW); // VBO for the indicesArray glGenBuffers(1, &_vboIndicesID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES_PER_VOXEL * sizeof(GLuint) * _maxVoxels, indicesArray, GL_STATIC_DRAW); // delete the indices and normals arrays that are no longer needed delete[] indicesArray; delete[] normalsArray; // create our simple fragment shader if we're the first system to init if (_perlinModulateProgram != 0) { return; } switchToResourcesParentIfRequired(); _perlinModulateProgram = new ProgramObject(); _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::updateFullVBOs() { updateVBOSegment(0, _voxelsInReadArrays); // consider the _readVoxelDirtyArray[] clean! memset(_readVoxelDirtyArray, false, _voxelsInReadArrays * sizeof(bool)); } void VoxelSystem::updatePartialVBOs() { glBufferIndex segmentStart = 0; bool inSegment = false; for (glBufferIndex i = 0; i < _voxelsInReadArrays; i++) { bool thisVoxelDirty = _readVoxelDirtyArray[i]; if (!inSegment) { if (thisVoxelDirty) { segmentStart = i; inSegment = true; _readVoxelDirtyArray[i] = false; // consider us clean! } } else { if (!thisVoxelDirty) { // If we got here because because this voxel is NOT dirty, so the last dirty voxel was the one before // this one and so that's where the "segment" ends updateVBOSegment(segmentStart, i - 1); inSegment = false; } _readVoxelDirtyArray[i] = false; // consider us clean! } } // if we got to the end of the array, and we're in an active dirty segment... if (inSegment) { updateVBOSegment(segmentStart, _voxelsInReadArrays - 1); inSegment = false; } } void VoxelSystem::updateVBOs() { static char buffer[40] = { 0 }; if (_renderWarningsOn) { sprintf(buffer, "updateVBOs() _readRenderFullVBO=%s", debug::valueOf(_readRenderFullVBO)); }; PerformanceWarning warn(_renderWarningsOn, buffer); // would like to include _callsToTreesToArrays if (_voxelsDirty) { if (_readRenderFullVBO) { updateFullVBOs(); } else { updatePartialVBOs(); } _voxelsDirty = false; _readRenderFullVBO = false; } _callsToTreesToArrays = 0; // clear it } void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) { int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); } void VoxelSystem::render(bool texture) { PerformanceWarning warn(_renderWarningsOn, "render()"); // get the lock so that the update thread won't change anything pthread_mutex_lock(&_bufferWriteLock); updateVBOs(); // tell OpenGL where to find vertex and color information glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); glVertexPointer(3, GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, _vboNormalsID); glNormalPointer(GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); applyScaleAndBindProgram(texture); // for performance, disable blending and enable backface culling glDisable(GL_BLEND); glEnable(GL_CULL_FACE); // draw the number of voxels we have glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesID); glDrawRangeElementsEXT(GL_TRIANGLES, 0, VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, 36 * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glEnable(GL_BLEND); glDisable(GL_CULL_FACE); removeScaleAndReleaseProgram(texture); // deactivate vertex and color arrays after drawing glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_COLOR_ARRAY); // bind with 0 to switch back to normal operation glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); pthread_mutex_unlock(&_bufferWriteLock); } void VoxelSystem::applyScaleAndBindProgram(bool texture) { glPushMatrix(); glScalef(_treeScale, _treeScale, _treeScale); if (texture) { _perlinModulateProgram->bind(); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID()); } } void VoxelSystem::removeScaleAndReleaseProgram(bool texture) { // scale back down to 1 so heads aren't massive glPopMatrix(); if (texture) { _perlinModulateProgram->release(); glBindTexture(GL_TEXTURE_2D, 0); } } int VoxelSystem::_nodeCount = 0; void VoxelSystem::killLocalVoxels() { _tree->eraseAllVoxels(); _voxelsInWriteArrays = _voxelsInReadArrays = 0; // better way to do this?? //setupNewVoxelsForDrawing(); } bool VoxelSystem::randomColorOperation(VoxelNode* node, void* extraData) { _nodeCount++; if (node->isColored()) { nodeColor newColor = { 255, randomColorValue(150), randomColorValue(150), 1 }; node->setColor(newColor); } return true; } void VoxelSystem::randomizeVoxelColors() { _nodeCount = 0; _tree->recurseTreeWithOperation(randomColorOperation); qDebug("setting randomized true color for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } bool VoxelSystem::falseColorizeRandomOperation(VoxelNode* node, void* extraData) { _nodeCount++; // always false colorize node->setFalseColor(255, randomColorValue(150), randomColorValue(150)); return true; // keep going! } void VoxelSystem::falseColorizeRandom() { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeRandomOperation); qDebug("setting randomized false color for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } bool VoxelSystem::trueColorizeOperation(VoxelNode* node, void* extraData) { _nodeCount++; node->setFalseColored(false); return true; } void VoxelSystem::trueColorize() { PerformanceWarning warn(true, "trueColorize()",true); _nodeCount = 0; _tree->recurseTreeWithOperation(trueColorizeOperation); qDebug("setting true color for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } // Will false colorize voxels that are not in view bool VoxelSystem::falseColorizeInViewOperation(VoxelNode* node, void* extraData) { const ViewFrustum* viewFrustum = (const ViewFrustum*) extraData; _nodeCount++; if (node->isColored()) { if (!node->isInView(*viewFrustum)) { // Out of view voxels are colored RED node->setFalseColor(255, 0, 0); } } return true; // keep going! } void VoxelSystem::falseColorizeInView(ViewFrustum* viewFrustum) { _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeInViewOperation,(void*)viewFrustum); qDebug("setting in view false color for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } // helper classes and args for falseColorizeBySource class groupColor { public: unsigned char red, green, blue; groupColor(unsigned char red, unsigned char green, unsigned char blue) : red(red), green(green), blue(blue) { }; groupColor() : red(0), green(0), blue(0) { }; }; class colorizeBySourceArgs { public: std::map<uint16_t, groupColor> colors; }; // Will false colorize voxels that are not in view bool VoxelSystem::falseColorizeBySourceOperation(VoxelNode* node, void* extraData) { colorizeBySourceArgs* args = (colorizeBySourceArgs*)extraData; _nodeCount++; if (node->isColored()) { // pick a color based on the source - we want each source to be obviously different uint16_t nodeID = node->getSourceID(); //printf("false colorizing from source %d, color: %d, %d, %d\n", nodeID, // args->colors[nodeID].red, args->colors[nodeID].green, args->colors[nodeID].blue); node->setFalseColor(args->colors[nodeID].red, args->colors[nodeID].green, args->colors[nodeID].blue); } return true; // keep going! } void VoxelSystem::falseColorizeBySource() { _nodeCount = 0; colorizeBySourceArgs args; const int NUMBER_OF_COLOR_GROUPS = 6; const unsigned char MIN_COLOR = 128; int voxelServerCount = 0; groupColor groupColors[NUMBER_OF_COLOR_GROUPS] = { groupColor(255, 0, 0), groupColor( 0, 255, 0), groupColor( 0, 0, 255), groupColor(255, 0, 255), groupColor( 0, 255, 255), groupColor(255, 255, 255) }; // create a bunch of colors we'll use during colorization NodeList* nodeList = NodeList::getInstance(); for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) { uint16_t nodeID = node->getNodeID(); int groupColor = voxelServerCount % NUMBER_OF_COLOR_GROUPS; args.colors[nodeID] = groupColors[groupColor]; //printf("assigning color for source %d, color: %d, %d, %d\n", nodeID, // args.colors[nodeID].red, args.colors[nodeID].green, args.colors[nodeID].blue); if (groupColors[groupColor].red > 0) { groupColors[groupColor].red = ((groupColors[groupColor].red - MIN_COLOR)/2) + MIN_COLOR; } if (groupColors[groupColor].green > 0) { groupColors[groupColor].green = ((groupColors[groupColor].green - MIN_COLOR)/2) + MIN_COLOR; } if (groupColors[groupColor].blue > 0) { groupColors[groupColor].blue = ((groupColors[groupColor].blue - MIN_COLOR)/2) + MIN_COLOR; } voxelServerCount++; } } _tree->recurseTreeWithOperation(falseColorizeBySourceOperation, &args); qDebug("setting false color by source for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } // Will false colorize voxels based on distance from view bool VoxelSystem::falseColorizeDistanceFromViewOperation(VoxelNode* node, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; if (node->isColored()) { float distance = node->distanceToCamera(*viewFrustum); _nodeCount++; float distanceRatio = (_minDistance == _maxDistance) ? 1 : (distance - _minDistance) / (_maxDistance - _minDistance); // We want to colorize this in 16 bug chunks of color const unsigned char maxColor = 255; const unsigned char colorBands = 16; const unsigned char gradientOver = 128; unsigned char colorBand = (colorBands * distanceRatio); node->setFalseColor((colorBand * (gradientOver / colorBands)) + (maxColor - gradientOver), 0, 0); } return true; // keep going! } float VoxelSystem::_maxDistance = 0.0; float VoxelSystem::_minDistance = FLT_MAX; // Helper function will get the distance from view range, would be nice if you could just keep track // of this as voxels are created and/or colored... seems like some transform math could do that so // we wouldn't need to do two passes of the tree bool VoxelSystem::getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData) { ViewFrustum* viewFrustum = (ViewFrustum*) extraData; // only do this for truly colored voxels... if (node->isColored()) { float distance = node->distanceToCamera(*viewFrustum); // calculate the range of distances if (distance > _maxDistance) { _maxDistance = distance; } if (distance < _minDistance) { _minDistance = distance; } _nodeCount++; } return true; // keep going! } void VoxelSystem::falseColorizeDistanceFromView(ViewFrustum* viewFrustum) { _nodeCount = 0; _maxDistance = 0.0; _minDistance = FLT_MAX; _tree->recurseTreeWithOperation(getDistanceFromViewRangeOperation,(void*)viewFrustum); qDebug("determining distance range for %d nodes\n", _nodeCount); _nodeCount = 0; _tree->recurseTreeWithOperation(falseColorizeDistanceFromViewOperation,(void*)viewFrustum); qDebug("setting in distance false color for %d nodes\n", _nodeCount); _tree->setDirtyBit(); setupNewVoxelsForDrawing(); } // combines the removeOutOfView args into a single class class removeOutOfViewArgs { public: VoxelSystem* thisVoxelSystem; VoxelNodeBag dontRecurseBag; unsigned long nodesScanned; unsigned long nodesRemoved; unsigned long nodesInside; unsigned long nodesIntersect; unsigned long nodesOutside; removeOutOfViewArgs(VoxelSystem* voxelSystem) : thisVoxelSystem(voxelSystem), dontRecurseBag(), nodesScanned(0), nodesRemoved(0), nodesInside(0), nodesIntersect(0), nodesOutside(0) { } }; // "Remove" voxels from the tree that are not in view. We don't actually delete them, // we remove them from the tree and place them into a holding area for later deletion bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) { removeOutOfViewArgs* args = (removeOutOfViewArgs*)extraData; // If our node was previously added to the don't recurse bag, then return false to // stop the further recursion. This means that the whole node and it's children are // known to be in view, so don't recurse them if (args->dontRecurseBag.contains(node)) { args->dontRecurseBag.remove(node); return false; // stop recursion } VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; args->nodesScanned++; // Need to operate on our child nodes, so we can remove them for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { VoxelNode* childNode = node->getChildAtIndex(i); if (childNode) { ViewFrustum::location inFrustum = childNode->inFrustum(*Application::getInstance()->getViewFrustum()); switch (inFrustum) { case ViewFrustum::OUTSIDE: { args->nodesOutside++; args->nodesRemoved++; node->removeChildAtIndex(i); thisVoxelSystem->_removedVoxels.insert(childNode); // by removing the child, it will not get recursed! } break; case ViewFrustum::INSIDE: { // if the child node is fully INSIDE the view, then there's no need to recurse it // because we know all it's children will also be in the view, so we want to // tell the caller to NOT recurse this child args->nodesInside++; args->dontRecurseBag.insert(childNode); } break; case ViewFrustum::INTERSECT: { // if the child node INTERSECTs the view, then we don't want to remove it because // it is at least partially in view. But we DO want to recurse the children because // some of them may not be in view... nothing specifically to do, just keep iterating // the children args->nodesIntersect++;