atlante45mergeb8c18be13 years agoHistory
1663 lines · 61.5 KB · C++
RawDownload
////  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" 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,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1,                             0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,1, 1,0,1, 1,1,1, 0,1,1 }; 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+ 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();    pthread_mutex_init(&_bufferWriteLock, NULL);    pthread_mutex_init(&_treeLock, NULL);     VoxelNode::addDeleteHook(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)));} void VoxelSystem::nodeDeleted(VoxelNode* node) {    if (node->isKnownBufferIndex() && (node->getVoxelSystem() == this)) {        freeBufferIndex(node->getBufferIndex());    }} // 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() {    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() {    if (_initialized) {        // Destroy  glBuffers        glDeleteBuffers(1, &_vboVerticesID);        glDeleteBuffers(1, &_vboNormalsID);        glDeleteBuffers(1, &_vboColorsID);        glDeleteBuffers(1, &_vboIndicesID);         delete[] _readVerticesArray;        delete[] _writeVerticesArray;        delete[] _readColorsArray;        delete[] _writeColorsArray;        delete[] _writeVoxelDirtyArray;        delete[] _readVoxelDirtyArray;    }     delete _tree;    pthread_mutex_destroy(&_bufferWriteLock);    pthread_mutex_destroy(&_treeLock);     VoxelNode::removeDeleteHook(this);} 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            ReadBitstreamToTreeParams args(NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceID());            _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args);        }            break;        case PACKET_TYPE_Z_COMMAND:             // the Z command is a special command that allows the sender to send high level semantic            // requests, like erase all, or add sphere scene, different receivers may handle these            // messages differently            char* packetData = (char *)sourceBuffer;            char* command = &packetData[numBytesPacketHeader]; // start of the command            int commandLength = strlen(command); // commands are null terminated strings            int totalLength = 1+commandLength+1;             qDebug("got Z message len(%d)= %s\n", numBytes, command);             while (totalLength <= numBytes) {                if (0==strcmp(command,(char*)"erase all")) {                    qDebug("got Z message == erase all\n");                    _tree->eraseAllVoxels();                    _voxelsInReadArrays = _voxelsInWriteArrays = 0; // better way to do this??                }                if (0==strcmp(command,(char*)"add scene")) {                    qDebug("got Z message == add scene - NOT SUPPORTED ON INTERFACE\n");                }                totalLength += commandLength+1;            }        break;    }     setupNewVoxelsForDrawing();        pthread_mutex_unlock(&_treeLock);     Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::VOXELS).updateValue(numBytes);     return numBytes;} void VoxelSystem::setupNewVoxelsForDrawing() {    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),                            "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated    uint64_t start = usecTimestampNow();    uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000;        // clear up the VBOs for any nodes that have been recently deleted.    clearFreeBufferIndexes();     bool iAmDebugging = false;  // if you're debugging set this to true, so you won't get skipped for slow debugging    if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {        return; // bail early, it hasn't been long enough since the last time we ran    }     uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000;    // If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view    if ((sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))            && !isViewChanging()) {        _lastViewCulling = start;         // When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove        // them from tree, this makes our tree caclulations faster, but doesn't require us to fully rebuild the VBOs (which        // can be expensive).        removeOutOfView();                // Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So,        // we should consider putting this someplace else... as this might be able to occur less frequently, and save us on        // VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary.        cleanupRemovedVoxels();         uint64_t endViewCulling = usecTimestampNow();        _lastViewCullingElapsed = (endViewCulling - start) / 1000;    }            bool didWriteFullVBO = _writeRenderFullVBO;    if (_tree->isDirty()) {        static char buffer[64] = { 0 };        if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) {            sprintf(buffer, "newTreeToArrays() _writeRenderFullVBO=%s", debug::valueOf(_writeRenderFullVBO));         };        PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer);        _callsToTreesToArrays++;        if (_writeRenderFullVBO) {            _voxelsInWriteArrays = 0; // reset our VBO            _freeIndexes.clear(); // reset our free indexes        }        _voxelsUpdated = newTreeToArrays(_tree->rootNode);        _tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean         if (_writeRenderFullVBO) {            _abandonedVBOSlots = 0; // reset the count of our abandoned slots, why is this here and not earlier????        }                // since we called treeToArrays, we can assume that our VBO is in sync, and so partial updates to the VBOs are        // ok again, until/unless we call removeOutOfView()         _writeRenderFullVBO = false;     } else {        _voxelsUpdated = 0;    }        // lock on the buffer write lock so we can't modify the data when the GPU is reading it    pthread_mutex_lock(&_bufferWriteLock);        if (_voxelsUpdated) {        _voxelsDirty=true;    }     // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated    copyWrittenDataToReadArrays(didWriteFullVBO);     pthread_mutex_unlock(&_bufferWriteLock);     uint64_t end = usecTimestampNow();    int elapsedmsec = (end - start) / 1000;    _setupNewVoxelsForDrawingLastFinished = end;    _setupNewVoxelsForDrawingLastElapsed = elapsedmsec;} void VoxelSystem::cleanupRemovedVoxels() {    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "cleanupRemovedVoxels()");    // This handles cleanup of voxels that were culled as part of our regular out of view culling operation    if (!_removedVoxels.isEmpty()) {        while (!_removedVoxels.isEmpty()) {            delete _removedVoxels.extract();        }        _writeRenderFullVBO = true; // if we remove voxels, we must update our full VBOs    }    // we also might have VBO slots that have been abandoned, if too many of our VBO slots    // are abandonded we want to rerender our full VBOs    const float TOO_MANY_ABANDONED_RATIO = 0.25f;    if (!_writeRenderFullVBO && (_abandonedVBOSlots > (_voxelsInWriteArrays * TOO_MANY_ABANDONED_RATIO))) {        _writeRenderFullVBO = true;    }} void VoxelSystem::copyWrittenDataToReadArraysFullVBOs() {    copyWrittenDataSegmentToReadArrays(0, _voxelsInWriteArrays - 1);     _voxelsInReadArrays = _voxelsInWriteArrays;        // clear our dirty flags    memset(_writeVoxelDirtyArray, false, _voxelsInWriteArrays * sizeof(bool));        // let the reader know to get the full array    _readRenderFullVBO = true;} void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() {    glBufferIndex segmentStart = 0;    bool inSegment = false;    for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) {        bool thisVoxelDirty = _writeVoxelDirtyArray[i];        _readVoxelDirtyArray[i] |= thisVoxelDirty;        _writeVoxelDirtyArray[i] = false;        if (!inSegment) {            if (thisVoxelDirty) {                segmentStart = i;                inSegment = true;            }        } 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                copyWrittenDataSegmentToReadArrays(segmentStart, i - 1);                inSegment = false;            }        }    }        // if we got to the end of the array, and we're in an active dirty segment...    if (inSegment) {        copyWrittenDataSegmentToReadArrays(segmentStart, _voxelsInWriteArrays - 1);    }     // update our length    _voxelsInReadArrays = _voxelsInWriteArrays;} void VoxelSystem::copyWrittenDataSegmentToReadArrays(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* readVerticesAt     = _readVerticesArray  + (segmentStart * VERTEX_POINTS_PER_VOXEL);    GLfloat* writeVerticesAt    = _writeVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);    memcpy(readVerticesAt, writeVerticesAt, segmentSizeBytes);     segmentStartAt          = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);    segmentSizeBytes        = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);    GLubyte* readColorsAt   = _readColorsArray   + (segmentStart * VERTEX_POINTS_PER_VOXEL);    GLubyte* writeColorsAt  = _writeColorsArray  + (segmentStart * VERTEX_POINTS_PER_VOXEL);    memcpy(readColorsAt, writeColorsAt, segmentSizeBytes);} void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),                            "copyWrittenDataToReadArrays()");    if (_voxelsDirty && _voxelsUpdated) {        if (fullVBOs) {            copyWrittenDataToReadArraysFullVBOs();        } else {            copyWrittenDataToReadArraysPartialVBOs();        }    }} int VoxelSystem::newTreeToArrays(VoxelNode* node) {    int   voxelsUpdated   = 0;    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);     node->setShouldRender(shouldRender);    // let children figure out their renderness    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();                voxelsUpdated += newTreeToArrays(childNode);                bool isShouldRender = childNode->getShouldRender();                if (wasShouldRender && !isShouldRender) {                    childrenGotHiddenCount++;                }            }        }        if (childrenGotHiddenCount > 0) {            node->setColorFromAverageOfChildren();        }    }    if (_writeRenderFullVBO) {        voxelsUpdated += updateNodeInArraysAsFullVBO(node);    } else {        voxelsUpdated += updateNodeInArraysAsPartialVBO(node);    }    node->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.        return voxelsUpdated;} int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {    // If we've run out of room, then just bail...    if (_voxelsInWriteArrays >= _maxVoxels) {        return 0;    }        if (node->getShouldRender()) {        glm::vec3 startVertex = node->getCorner();        float voxelScale = node->getScale();        glBufferIndex nodeIndex = getNextBufferIndex();         // populate the array with points for the 8 vertices        // and RGB color for each added vertex        updateNodeInArrays(nodeIndex, startVertex, voxelScale, node->getColor());        node->setBufferIndex(nodeIndex);        node->setVoxelSystem(this);        return 1; // rendered    } else {        node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN);        node->setVoxelSystem(NULL);    }        return 0; // not-rendered} int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {    // If we've run out of room, then just bail...    if (_voxelsInWriteArrays >= _maxVoxels) {        return 0;    }        // Now, if we've changed any attributes (our renderness, our color, etc) then update the Arrays...    if (node->isDirty()) {        glm::vec3 startVertex;        float voxelScale = 0;

Lines 1–500 of 1663.