diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7c7ab7aab6..439cbbebf2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -356,8 +356,9 @@ void Application::initializeGL() { void Application::paintGL() { PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings)); - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::paintGL()"); - + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::paintGL()"); + glEnable(GL_LINE_SMOOTH); if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { @@ -545,6 +546,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier); + bool isMeta = event->modifiers().testFlag(Qt::ControlModifier); switch (event->key()) { case Qt::Key_Shift: if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) { @@ -619,8 +621,10 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_S: - if (isShifted) { + if (isShifted && !isMeta) { _voxels.collectStatsForTreesAndVBOs(); + } else if (isShifted && isMeta) { + Menu::getInstance()->triggerOption(MenuOption::SuppressShortTimings); } else if (_nudgeStarted) { if (_lookingAlongX) { if (_lookingAwayFromOrigin) { @@ -1238,6 +1242,8 @@ static glm::vec3 getFaceVector(BoxFace face) { } void Application::idle() { + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::idle()"); timeval check; gettimeofday(&check, NULL); @@ -1252,7 +1258,7 @@ void Application::idle() { _glWidget->updateGL(); _lastTimeUpdated = check; _idleLoopStdev.addValue(timeSinceLastUpdate); - + // Record standard deviation and reset counter if needed const int STDEV_SAMPLES = 500; if (_idleLoopStdev.getSamples() > STDEV_SAMPLES) { @@ -1783,7 +1789,28 @@ void Application::renderFollowIndicator() { } } +void Application::updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection) { + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); + NodeList* nodeList = NodeList::getInstance(); + + for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + node->lock(); + if (node->getLinkedData() != NULL) { + Avatar *avatar = (Avatar *)node->getLinkedData(); + if (!avatar->isInitialized()) { + avatar->init(); + } + avatar->simulate(deltaTime, NULL); + avatar->setMouseRay(mouseRayOrigin, mouseRayDirection); + } + node->unlock(); + } +} + void Application::update(float deltaTime) { + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::update()"); // Use Transmitter Hand to move hand if connected, else use mouse if (_myTransmitter.isConnected()) { @@ -1884,6 +1911,8 @@ void Application::update(float deltaTime) { (fabs(_myAvatar.getVelocity().x) + fabs(_myAvatar.getVelocity().y) + fabs(_myAvatar.getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) { + PerformanceWarning warn(showWarnings, "Application::update()... findRayIntersection()"); + if (_voxels.findRayIntersection(mouseRayOrigin, mouseRayDirection, _mouseVoxel, distance, face)) { if (distance < MAX_VOXEL_EDIT_DISTANCE) { // find the nearest voxel with the desired scale @@ -1991,22 +2020,11 @@ void Application::update(float deltaTime) { _voxelProcessor.threadRoutine(); _voxelEditSender.threadRoutine(); } + //loop through all the other avatars and simulate them... - NodeList* nodeList = NodeList::getInstance(); - for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { - node->lock(); - if (node->getLinkedData() != NULL) { - Avatar *avatar = (Avatar *)node->getLinkedData(); - if (!avatar->isInitialized()) { - avatar->init(); - } - avatar->simulate(deltaTime, NULL); - avatar->setMouseRay(mouseRayOrigin, mouseRayDirection); - } - node->unlock(); - } - + updateAvatars(deltaTime, mouseRayOrigin, mouseRayDirection); + // Simulate myself if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) { _myAvatar.setGravity(_environment.getGravity(_myAvatar.getPosition())); @@ -2041,6 +2059,7 @@ void Application::update(float deltaTime) { if (_voxels.findRayIntersection(_transmitterPickStart, direction, detail, distance, face)) { minDistance = min(minDistance, distance); } + NodeList* nodeList = NodeList::getInstance(); for(NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { node->lock(); if (node->getLinkedData() != NULL) { @@ -2134,6 +2153,8 @@ void Application::update(float deltaTime) { } void Application::updateAvatar(float deltaTime) { + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "Application::updateAvatar()"); // rotate body yaw for yaw received from multitouch _myAvatar.setOrientation(_myAvatar.getOrientation() @@ -2990,51 +3011,54 @@ void Application::displayStats() { std::stringstream voxelStats; voxelStats.precision(4); voxelStats << "Voxels Rendered: " << _voxels.getVoxelsRendered() / 1000.f << "K " << + "Written: " << _voxels.getVoxelsWritten()/1000.f << "K " << "Updated: " << _voxels.getVoxelsUpdated()/1000.f << "K " << "Max: " << _voxels.getMaxVoxels()/1000.f << "K "; statsVerticalOffset += PELS_PER_LINE; - drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str()); voxelStats.str(""); voxelStats << - "Voxels Memory Nodes: " << VoxelNode::getVoxelMemoryUsage() / 1000000.f << "MB " - "Octcodes: " << VoxelNode::getOctcodeMemoryUsage() / 1000000.f << "MB " + "Voxels Memory Nodes: " << VoxelNode::getTotalMemoryUsage() / 1000000.f << "MB " "Geometry RAM: " << _voxels.getVoxelMemoryUsageRAM() / 1000000.f << "MB " << "VBO: " << _voxels.getVoxelMemoryUsageVBO() / 1000000.f << "MB "; if (_voxels.hasVoxelMemoryUsageGPU()) { voxelStats << "GPU: " << _voxels.getVoxelMemoryUsageGPU() / 1000000.f << "MB "; } - - // Some debugging for memory usage of VoxelNodes - //voxelStats << "VoxelNode size: " << sizeof(VoxelNode) << " bytes "; - //voxelStats << "AABox size: " << sizeof(AABox) << " bytes "; - statsVerticalOffset += PELS_PER_LINE; - drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str()); + + voxelStats.str(""); + voxelStats << + "Local Voxels Total: " << VoxelNode::getNodeCount() << ", " << + "Internal: " << VoxelNode::getInternalNodeCount() << " , " << + "Leaves: " << VoxelNode::getLeafNodeCount() << ""; + statsVerticalOffset += PELS_PER_LINE; + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str()); voxelStats.str(""); char* voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_VOXELS); voxelStats << "Voxels Sent from Server: " << voxelDetails; statsVerticalOffset += PELS_PER_LINE; - drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str()); voxelStats.str(""); voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_ELAPSED); voxelStats << "Scene Send Time from Server: " << voxelDetails; statsVerticalOffset += PELS_PER_LINE; - drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str()); voxelStats.str(""); voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_ENCODE); voxelStats << "Encode Time on Server: " << voxelDetails; statsVerticalOffset += PELS_PER_LINE; - drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str()); voxelStats.str(""); voxelDetails = _voxelSceneStats.getItemValue(VoxelSceneStats::ITEM_MODE); voxelStats << "Sending Mode: " << voxelDetails; statsVerticalOffset += PELS_PER_LINE; - drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str()); + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)voxelStats.str().c_str()); Node *avatarMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AVATAR_MIXER); char avatarMixerStats[200]; @@ -3049,7 +3073,7 @@ void Application::displayStats() { statsVerticalOffset += PELS_PER_LINE; drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, avatarMixerStats); statsVerticalOffset += PELS_PER_LINE; - drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char *)LeapManager::statusString().c_str()); + drawtext(10, statsVerticalOffset, 0.10f, 0, 1.0, 0, (char*)LeapManager::statusString().c_str()); if (_perfStatsOn) { // Get the PerfStats group details. We need to allocate and array of char* long enough to hold 1+groups @@ -3235,7 +3259,6 @@ void Application::renderCoverageMapsRecursively(CoverageMap* map) { -///////////////////////////////////////////////////////////////////////////////////// // renderViewFrustum() // // Description: this will render the view frustum bounds for EITHER the head @@ -3317,7 +3340,7 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_PLANES || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_FAR_PLANE) { // viewFrustum.getFar plane - bottom edge - glColor3f(0,1,0); // GREEN!!! + glColor3f(0,1,0); glVertex3f(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); glVertex3f(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z); @@ -3685,6 +3708,9 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng // Receive packets from other nodes/servers and decide what to do with them! void* Application::networkReceive(void* args) { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::networkReceive()"); + sockaddr senderAddress; ssize_t bytesReceived; @@ -3712,6 +3738,9 @@ void* Application::networkReceive(void* args) { case PACKET_TYPE_ERASE_VOXEL: case PACKET_TYPE_VOXEL_STATS: case PACKET_TYPE_ENVIRONMENT_DATA: { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), + "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); + // add this packet to our list of voxel packets and process them on the voxel processing app->_voxelProcessor.queueReceivedPacket(senderAddress, app->_incomingPacket, bytesReceived); break; diff --git a/interface/src/Application.h b/interface/src/Application.h index 0aae683844..0a563105c1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -216,6 +216,7 @@ private: void renderLookatIndicator(glm::vec3 pointOfInterest, Camera& whichCamera); void renderFollowIndicator(); void updateAvatar(float deltaTime); + void updateAvatars(float deltaTime, glm::vec3 mouseRayOrigin, glm::vec3 mouseRayDirection); void loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum); void displayOculus(Camera& whichCamera); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index bfafd873ef..5a217a71b0 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -275,16 +275,25 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelsAsPoints, 0, false, appInstance->getVoxels(), SLOT(setVoxelsAsPoints(bool))); - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::FastVoxelPipeline, 0, - false, appInstance->getVoxels(), SLOT(setUseFastVoxelPipeline(bool))); - - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontRemoveOutOfView); - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::HideOutOfView); - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::ConstantCulling); - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AutomaticallyAuditTree); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion); - + + QMenu* cullingOptionsMenu = voxelOptionsMenu->addMenu("Culling Options"); + addDisabledActionAndSeparator(cullingOptionsMenu, "Standard Settings"); + addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::OldVoxelCullingMode, 0, + false, this, SLOT(setOldVoxelCullingMode(bool))); + + addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::NewVoxelCullingMode, 0, + false, this, SLOT(setNewVoxelCullingMode(bool))); + + addDisabledActionAndSeparator(cullingOptionsMenu, "Individual Option Settings"); + addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::FastVoxelPipeline, 0, + false, appInstance->getVoxels(), SLOT(setUseFastVoxelPipeline(bool))); + addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::DontRemoveOutOfView); + addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::HideOutOfView); + addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::UseDeltaFrustumInHide); + addCheckableActionToQMenuAndActionHash(cullingOptionsMenu, MenuOption::ConstantCulling); + QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options"); @@ -377,8 +386,17 @@ Menu::Menu() : QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools"); - addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings); - addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::SuppressShortTimings); + addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings, Qt::CTRL | Qt::SHIFT | Qt::Key_P); + addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::SuppressShortTimings, Qt::CTRL | Qt::SHIFT | Qt::Key_S); + + addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::AutomaticallyAuditTree); + + + addActionToQMenuAndActionHash(renderDebugMenu, + MenuOption::ShowAllLocalVoxels, + Qt::CTRL | Qt::Key_A, + appInstance->getVoxels(), + SLOT(showAllLocalVoxels())); addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::KillLocalVoxels, @@ -1085,4 +1103,30 @@ void Menu::updateFrustumRenderModeAction() { } } +void Menu::setOldVoxelCullingMode(bool oldMode) { + setVoxelCullingMode(oldMode); +} +void Menu::setNewVoxelCullingMode(bool newMode) { + setVoxelCullingMode(!newMode); +} + +/// This will switch on or off several different individual settings options all at once based on choosing with Old or New +/// voxel culling mode. +void Menu::setVoxelCullingMode(bool oldMode) { + const QString menus[] = { MenuOption::FastVoxelPipeline, MenuOption::DontRemoveOutOfView, MenuOption::HideOutOfView, + MenuOption::UseDeltaFrustumInHide, MenuOption::ConstantCulling}; + bool oldModeValue[] = { false, false, false, false, false }; + bool newModeValue[] = { true, true, true, true, true }; + + for (int i = 0; i < sizeof(menus) / sizeof(menus[0]); i++) { + bool desiredValue = oldMode ? oldModeValue[i] : newModeValue[i]; + if (isOptionChecked(menus[i]) != desiredValue) { + triggerOption(menus[i]); + } + } + + // set the checkmarks accordingly... + _actionHash.value(MenuOption::OldVoxelCullingMode)->setChecked(oldMode); + _actionHash.value(MenuOption::NewVoxelCullingMode)->setChecked(!oldMode); +} diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 86e40ee928..798b74c362 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -81,6 +81,8 @@ private slots: void chooseVoxelPaintColor(); void runTests(); void resetSwatchColors(); + void setOldVoxelCullingMode(bool oldMode); + void setNewVoxelCullingMode(bool newMode); private: static Menu* _instance; @@ -108,6 +110,7 @@ private: const char* member = NULL); void updateFrustumRenderModeAction(); + void setVoxelCullingMode(bool oldMode); QHash _actionHash; int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback @@ -182,9 +185,11 @@ namespace MenuOption { const QString LookAtVectors = "Look-at Vectors"; const QString LowRes = "Lower Resolution While Moving"; const QString Mirror = "Mirror"; + const QString NewVoxelCullingMode = "New Voxel Culling Mode"; const QString NudgeVoxels = "Nudge"; const QString OcclusionCulling = "Occlusion Culling"; const QString OffAxisProjection = "Off-Axis Projection"; + const QString OldVoxelCullingMode = "Old Voxel Culling Mode"; const QString TurnWithHead = "Turn using Head"; const QString Oscilloscope = "Audio Oscilloscope"; const QString Pair = "Pair"; @@ -198,6 +203,7 @@ namespace MenuOption { const QString SendVoxelColors = "Colored Voxels"; const QString SettingsImport = "Import Settings"; const QString SettingsExport = "Export Settings"; + const QString ShowAllLocalVoxels = "Show All Local Voxels"; const QString ShowTrueColors = "Show TRUE Colors"; const QString SimulateLeapHand = "Simulate Leap Hand"; const QString SkeletonTracking = "Skeleton Tracking"; @@ -210,6 +216,7 @@ namespace MenuOption { const QString TreeStats = "Calculate Tree Stats"; const QString TransmitterDrive = "Transmitter Drive"; const QString Quit = "Quit"; + const QString UseDeltaFrustumInHide = "Use Delta View Frustums when Culling"; const QString UseVoxelShader = "Use Voxel Shader"; const QString VoxelsAsPoints = "Draw Voxels as Points"; const QString Voxels = "Voxels"; diff --git a/interface/src/VoxelPacketProcessor.cpp b/interface/src/VoxelPacketProcessor.cpp index 9e189aac64..76075c6fa5 100644 --- a/interface/src/VoxelPacketProcessor.cpp +++ b/interface/src/VoxelPacketProcessor.cpp @@ -17,6 +17,11 @@ void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "VoxelPacketProcessor::processPacket()"); + + const int WAY_BEHIND = 300; + if (packetsToProcessCount() > WAY_BEHIND && Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { + qDebug("VoxelPacketProcessor::processPacket() packets to process=%d\n", packetsToProcessCount()); + } ssize_t messageLength = packetLength; Application* app = Application::getInstance(); diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index bc2d353bf9..54fdd50976 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -73,6 +73,7 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) _writeRenderFullVBO = true; _readRenderFullVBO = true; _tree = new VoxelTree(); + _tree->rootNode->setVoxelSystem(this); pthread_mutex_init(&_bufferWriteLock, NULL); pthread_mutex_init(&_treeLock, NULL); @@ -106,6 +107,8 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) _inSetupNewVoxelsForDrawing = false; _useFastVoxelPipeline = false; + + _culledOnce = false; } void VoxelSystem::voxelDeleted(VoxelNode* node) { @@ -133,7 +136,7 @@ void VoxelSystem::voxelUpdated(VoxelNode* node) { } if (node->getVoxelSystem() == this) { - bool shouldRender = false; // assume we don't need to render it + 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); @@ -228,16 +231,21 @@ void VoxelSystem::freeBufferIndex(glBufferIndex 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()"); + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "clearFreeBufferIndexes()"); _voxelsInWriteArrays = 0; // reset our VBO _abandonedVBOSlots = 0; // clear out freeIndexes - pthread_mutex_lock(&_freeIndexLock); - _freeIndexes.clear(); + { + PerformanceWarning warn(showWarnings,"clearFreeBufferIndexes() : pthread_mutex_lock(&_freeIndexLock)"); + pthread_mutex_lock(&_freeIndexLock); + } + { + PerformanceWarning warn(showWarnings,"clearFreeBufferIndexes() : _freeIndexes.clear()"); + _freeIndexes.clear(); + } pthread_mutex_unlock(&_freeIndexLock); - - clearAllNodesBufferIndex(); } VoxelSystem::~VoxelSystem() { @@ -744,7 +752,6 @@ void VoxelSystem::checkForCulling() { && !isViewChanging() ) ) { - _lastViewCulling = start; // When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove @@ -949,6 +956,12 @@ int VoxelSystem::forceRemoveNodeFromArrays(VoxelNode* node) { int VoxelSystem::updateNodeInArrays(VoxelNode* node, bool reuseIndex, bool forceDraw) { // If we've run out of room, then just bail... if (_voxelsInWriteArrays >= _maxVoxels) { + // We need to think about what else we can do in this case. This basically means that all of our available + // VBO slots are used up, but we're trying to render more voxels. At this point, if this happens we'll just + // not render these Voxels. We need to think about ways to keep the entire scene intact but maybe lower quality + // possibly shifting down to lower LOD or something. This debug message is to help identify, if/when/how this + // state actually occurs. + qDebug("OHHHH NOOOOOO!!!! updateNodeInArrays() BAILING (_voxelsInWriteArrays >= _maxVoxels)\n"); return 0; } @@ -1061,10 +1074,24 @@ void VoxelSystem::changeTree(VoxelTree* newTree) { } void VoxelSystem::updateFullVBOs() { - updateVBOSegment(0, _voxelsInReadArrays); + bool outputWarning = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(outputWarning, "updateFullVBOs()"); + + { + static char buffer[128] = { 0 }; + if (outputWarning) { + sprintf(buffer, "updateFullVBOs() : updateVBOSegment(0, _voxelsInReadArrays=%lu);", _voxelsInReadArrays); + }; + + PerformanceWarning warn(outputWarning,buffer); + updateVBOSegment(0, _voxelsInReadArrays); + } - // consider the _readVoxelDirtyArray[] clean! - memset(_readVoxelDirtyArray, false, _voxelsInReadArrays * sizeof(bool)); + { + PerformanceWarning warn(outputWarning,"updateFullVBOs() : memset(_readVoxelDirtyArray...)"); + // consider the _readVoxelDirtyArray[] clean! + memset(_readVoxelDirtyArray, false, _voxelsInReadArrays * sizeof(bool)); + } } void VoxelSystem::updatePartialVBOs() { @@ -1116,6 +1143,9 @@ void VoxelSystem::updateVBOs() { } void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) { + bool showWarning = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarning, "updateVBOSegment()"); + if (_useVoxelShader) { int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * sizeof(VoxelShaderVBOData); @@ -1130,18 +1160,36 @@ void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex seg GLintptr segmentStartAt = segmentStart * vertexPointsPerVoxel * sizeof(GLfloat); GLsizeiptr segmentSizeBytes = segmentLength * vertexPointsPerVoxel * sizeof(GLfloat); GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * vertexPointsPerVoxel); - glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); - glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); + + { + PerformanceWarning warn(showWarning, "updateVBOSegment() : glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);"); + glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); + } + + { + PerformanceWarning warn(showWarning, "updateVBOSegment() : glBufferSubData() _vboVerticesID);"); + glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); + } + segmentStartAt = segmentStart * vertexPointsPerVoxel * sizeof(GLubyte); segmentSizeBytes = segmentLength * vertexPointsPerVoxel * sizeof(GLubyte); GLubyte* readColorsFrom = _readColorsArray + (segmentStart * vertexPointsPerVoxel); - glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); - glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); + + { + PerformanceWarning warn(showWarning, "updateVBOSegment() : glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);"); + glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); + } + + { + PerformanceWarning warn(showWarning, "updateVBOSegment() : glBufferSubData() _vboColorsID);"); + glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); + } } } void VoxelSystem::render(bool texture) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "render()"); + bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showWarnings, "render()"); // If we got here and we're not initialized then bail! if (!_initialized) { @@ -1153,8 +1201,7 @@ void VoxelSystem::render(bool texture) { bool dontCallOpenGLDraw = Menu::getInstance()->isOptionChecked(MenuOption::DontCallOpenGLForVoxels); // if not don't... then do... if (_useVoxelShader) { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "render().. _useVoxelShader openGL.."); + PerformanceWarning warn(showWarnings,"render().. _useVoxelShader openGL.."); //Define this somewhere in your header file @@ -1200,68 +1247,78 @@ void VoxelSystem::render(bool texture) { glDisableVertexAttribArray(attributeLocation); } } else { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "render().. openGL..."); + PerformanceWarning warn(showWarnings, "render().. TRIANGLES..."); - // tell OpenGL where to find vertex and color information - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); + { + PerformanceWarning warn(showWarnings,"render().. setup before glDrawRangeElementsEXT()..."); + + // tell OpenGL where to find vertex and color information + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); - glVertexPointer(3, GL_FLOAT, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); + glVertexPointer(3, GL_FLOAT, 0, 0); - glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); - glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); + glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0); - applyScaleAndBindProgram(texture); + applyScaleAndBindProgram(texture); - // for performance, enable backface culling - glEnable(GL_CULL_FACE); + // for performance, enable backface culling + glEnable(GL_CULL_FACE); + } // draw voxels in 6 passes if (!dontCallOpenGLDraw) { + PerformanceWarning warn(showWarnings, "render().. glDrawRangeElementsEXT()..."); + glNormal3f(0,1.0f,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesTop); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glNormal3f(0,-1.0f,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesBottom); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glNormal3f(-1.0f,0,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesLeft); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glNormal3f(1.0f,0,0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesRight); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glNormal3f(0,0,-1.0f); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesFront); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); glNormal3f(0,0,1.0f); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vboIndicesBack); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, INDICES_PER_FACE * _voxelsInReadArrays - 1, + glDrawRangeElementsEXT(GL_TRIANGLES, 0, GLOBAL_NORMALS_VERTICES_PER_VOXEL * _voxelsInReadArrays - 1, INDICES_PER_FACE * _voxelsInReadArrays, GL_UNSIGNED_INT, 0); } - glDisable(GL_CULL_FACE); + { + PerformanceWarning warn(showWarnings, "render().. cleanup after glDrawRangeElementsEXT()..."); + + glDisable(GL_CULL_FACE); - removeScaleAndReleaseProgram(texture); + removeScaleAndReleaseProgram(texture); - // deactivate vertex and color arrays after drawing - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); + // deactivate vertex and color arrays after drawing + glDisableClientState(GL_VERTEX_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); + // bind with 0 to switch back to normal operation + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } } } @@ -1526,11 +1583,11 @@ public: VoxelSystem* thisVoxelSystem; ViewFrustum thisViewFrustum; VoxelNodeBag dontRecurseBag; - unsigned long nodesScanned; - unsigned long nodesRemoved; - unsigned long nodesInside; - unsigned long nodesIntersect; - unsigned long nodesOutside; + unsigned long nodesScanned; + unsigned long nodesRemoved; + unsigned long nodesInside; + unsigned long nodesIntersect; + unsigned long nodesOutside; VoxelNode* insideRoot; VoxelNode* outsideRoot; @@ -1611,10 +1668,10 @@ bool VoxelSystem::removeOutOfViewOperation(VoxelNode* node, void* extraData) { bool VoxelSystem::isViewChanging() { bool result = false; // assume the best - // If our viewFrustum has changed since our _lastKnowViewFrustum - if (!_lastKnowViewFrustum.matches(_viewFrustum)) { + // If our viewFrustum has changed since our _lastKnownViewFrustum + if (!_lastKnownViewFrustum.matches(_viewFrustum)) { result = true; - _lastKnowViewFrustum = *_viewFrustum; // save last known + _lastKnownViewFrustum = *_viewFrustum; // save last known } return result; } @@ -1627,7 +1684,7 @@ bool VoxelSystem::hasViewChanged() { return false; } - // If our viewFrustum has changed since our _lastKnowViewFrustum + // If our viewFrustum has changed since our _lastKnownViewFrustum if (!_lastStableViewFrustum.matches(_viewFrustum)) { result = true; _lastStableViewFrustum = *_viewFrustum; // save last stable @@ -1652,27 +1709,92 @@ void VoxelSystem::removeOutOfView() { } } +// combines the removeOutOfView args into a single class +class showAllLocalVoxelsArgs { +public: + VoxelSystem* thisVoxelSystem; + ViewFrustum thisViewFrustum; + unsigned long nodesScanned; + + showAllLocalVoxelsArgs(VoxelSystem* voxelSystem) : + thisVoxelSystem(voxelSystem), + thisViewFrustum(*voxelSystem->getViewFrustum()), + nodesScanned(0) + { + } +}; + +void VoxelSystem::showAllLocalVoxels() { + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "showAllLocalVoxels()"); + showAllLocalVoxelsArgs args(this); + _tree->recurseTreeWithOperation(showAllLocalVoxelsOperation,(void*)&args); + + bool showRemoveDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + if (showRemoveDebugDetails) { + qDebug("showAllLocalVoxels() scanned=%ld \n",args.nodesScanned ); + } +} + +bool VoxelSystem::showAllLocalVoxelsOperation(VoxelNode* node, void* extraData) { + showAllLocalVoxelsArgs* args = (showAllLocalVoxelsArgs*)extraData; + + args->nodesScanned++; + + bool shouldRender = true; // node->calculateShouldRender(&args->thisViewFrustum); + node->setShouldRender(shouldRender); + + if (shouldRender) { + bool falseColorize = false; + if (falseColorize) { + node->setFalseColor(0,0,255); // false colorize + } + // These are both needed to force redraw... + node->setDirtyBit(); + node->markWithChangedTime(); + } + + return true; // keep recursing! +} + + // combines the removeOutOfView args into a single class class hideOutOfViewArgs { public: - VoxelSystem* thisVoxelSystem; - VoxelTree* tree; - ViewFrustum thisViewFrustum; - unsigned long nodesScanned; - unsigned long nodesRemoved; - unsigned long nodesInside; - unsigned long nodesIntersect; - unsigned long nodesOutside; + VoxelSystem* thisVoxelSystem; + VoxelTree* tree; + ViewFrustum thisViewFrustum; + ViewFrustum lastViewFrustum; + bool culledOnce; + bool wantDeltaFrustums; + unsigned long nodesScanned; + unsigned long nodesRemoved; + unsigned long nodesInside; + unsigned long nodesIntersect; + unsigned long nodesOutside; + unsigned long nodesInsideInside; + unsigned long nodesIntersectInside; + unsigned long nodesOutsideInside; + unsigned long nodesInsideOutside; + unsigned long nodesOutsideOutside; - hideOutOfViewArgs(VoxelSystem* voxelSystem, VoxelTree* tree, bool widenViewFrustum = true) : + hideOutOfViewArgs(VoxelSystem* voxelSystem, VoxelTree* tree, + bool culledOnce, bool widenViewFrustum, bool wantDeltaFrustums) : thisVoxelSystem(voxelSystem), tree(tree), thisViewFrustum(*voxelSystem->getViewFrustum()), + lastViewFrustum(*voxelSystem->getLastCulledViewFrustum()), + culledOnce(culledOnce), + wantDeltaFrustums(wantDeltaFrustums), nodesScanned(0), nodesRemoved(0), nodesInside(0), nodesIntersect(0), - nodesOutside(0) + nodesOutside(0), + nodesInsideInside(0), + nodesIntersectInside(0), + nodesOutsideInside(0), + nodesInsideOutside(0), + nodesOutsideOutside(0) { // Widen the FOV for trimming if (widenViewFrustum) { @@ -1685,34 +1807,88 @@ public: }; void VoxelSystem::hideOutOfView() { - PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "hideOutOfView()"); - hideOutOfViewArgs args(this, this->_tree, true); // widen to match server! + bool showDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); + PerformanceWarning warn(showDebugDetails, "hideOutOfView()", showDebugDetails); + bool widenFrustum = true; + bool wantDeltaFrustums = false; // Menu::getInstance()->isOptionChecked(MenuOption::UseDeltaFrustumInHide); + hideOutOfViewArgs args(this, this->_tree, _culledOnce, widenFrustum, wantDeltaFrustums); + + const bool wantViewFrustumDebugging = false; // change to true for additional debugging + if (wantViewFrustumDebugging) { + args.thisViewFrustum.printDebugDetails(); + if (_culledOnce) { + args.lastViewFrustum.printDebugDetails(); + } + } + + if (_culledOnce && args.lastViewFrustum.matches(args.thisViewFrustum)) { + //printf("view frustum hasn't changed BAIL!!!\n"); + return; + } + + // Changed hideOutOfView() to support "delta" view frustums and only hide/show items that are in the difference + // between the two view frustums. There are some potential problems with this idea... + // + // 1) This might work well for rotating, but what about moving forward? + // in the move forward case, you'll get new voxel details, but those + // new voxels will be in the last view... does that work? This works + // ok for now because voxel server resends them and so they get redisplayed, + // but this will not work if we update the voxel server to send less data. + // + // 2) what about voxels coming in from the network that are OUTSIDE of the view + // frustum... they don't get hidden... and so we can't assume they are correctly + // hidden... we could solve this with checking in view on voxelUpdated... + // _tree->recurseTreeWithOperation(hideOutOfViewOperation,(void*)&args); + _lastCulledViewFrustum = args.thisViewFrustum; // save last stable + _culledOnce = true; if (args.nodesRemoved) { _tree->setDirtyBit(); setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY); } - - bool showRemoveDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - if (showRemoveDebugDetails) { + if (showDebugDetails) { qDebug("hideOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld\n", args.nodesScanned, args.nodesRemoved, args.nodesInside, args.nodesIntersect, args.nodesOutside ); + qDebug(" inside/inside=%ld intersect/inside=%ld outside/outside=%ld\n", + args.nodesInsideInside, args.nodesIntersectInside, args.nodesOutsideOutside + ); } } bool VoxelSystem::hideAllSubTreeOperation(VoxelNode* node, void* extraData) { hideOutOfViewArgs* args = (hideOutOfViewArgs*)extraData; + // If we've culled at least once, then we will use the status of this voxel in the last culled frustum to determine + // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not + // consider that case. + ViewFrustum::location inLastCulledFrustum; + + if (args->culledOnce && args->wantDeltaFrustums) { + inLastCulledFrustum = node->inFrustum(args->lastViewFrustum); + + // if this node is fully OUTSIDE our last culled view frustum, then we don't need to recurse further + if (inLastCulledFrustum == ViewFrustum::OUTSIDE) { + args->nodesOutsideOutside++; + return false; + } + } + args->nodesOutside++; if (node->isKnownBufferIndex()) { args->nodesRemoved++; - VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; - thisVoxelSystem->_voxelsUpdated += thisVoxelSystem->forceRemoveNodeFromArrays(node); - thisVoxelSystem->setupNewVoxelsForDrawingSingleNode(); + bool falseColorize = false; + if (falseColorize) { + node->setFalseColor(255,0,0); // false colorize + } else { + VoxelSystem* thisVoxelSystem = args->thisVoxelSystem; + thisVoxelSystem->_voxelsUpdated += thisVoxelSystem->forceRemoveNodeFromArrays(node); + thisVoxelSystem->setupNewVoxelsForDrawingSingleNode(); + } + } return true; @@ -1721,17 +1897,39 @@ bool VoxelSystem::hideAllSubTreeOperation(VoxelNode* node, void* extraData) { bool VoxelSystem::showAllSubTreeOperation(VoxelNode* node, void* extraData) { hideOutOfViewArgs* args = (hideOutOfViewArgs*)extraData; + // If we've culled at least once, then we will use the status of this voxel in the last culled frustum to determine + // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not + // consider that case. + ViewFrustum::location inLastCulledFrustum; + + if (args->culledOnce && args->wantDeltaFrustums) { + inLastCulledFrustum = node->inFrustum(args->lastViewFrustum); + + // if this node is fully inside our last culled view frustum, then we don't need to recurse further + if (inLastCulledFrustum == ViewFrustum::INSIDE) { + args->nodesInsideInside++; + return false; + } + } + args->nodesInside++; - if (node->getShouldRender() && !node->isKnownBufferIndex()) { - node->setDirtyBit(); // will this make it draw! + bool shouldRender = node->calculateShouldRender(&args->thisViewFrustum); + node->setShouldRender(shouldRender); + + if (shouldRender && !node->isKnownBufferIndex()) { + bool falseColorize = false; + if (falseColorize) { + node->setFalseColor(0,0,255); // false colorize + } + // These are both needed to force redraw... + node->setDirtyBit(); + node->markWithChangedTime(); } return true; // keep recursing! } - - // "hide" voxels in the VBOs that are still in the tree that but not in view. // We don't remove them from the tree, we don't delete them, we do remove them // from the VBOs and mark them as such in the tree. @@ -1741,26 +1939,66 @@ bool VoxelSystem::hideOutOfViewOperation(VoxelNode* node, void* extraData) { // If we're still recursing the tree using this operator, then we don't know if we're inside or outside... // so before we move forward we need to determine our frustum location ViewFrustum::location inFrustum = node->inFrustum(args->thisViewFrustum); + + // If we've culled at least once, then we will use the status of this voxel in the last culled frustum to determine + // how to proceed. If we've never culled, then we just consider all these voxels to be UNKNOWN so that we will not + // consider that case. + ViewFrustum::location inLastCulledFrustum; + + if (args->culledOnce && args->wantDeltaFrustums) { + inLastCulledFrustum = node->inFrustum(args->lastViewFrustum); + } // ok, now do some processing for this node... switch (inFrustum) { case ViewFrustum::OUTSIDE: { - // if this node is fully OUTSIDE the view, then we know that ALL of it's children are also fully OUTSIDE - // so we can recurse the children and simply mark them as hidden + + // If this node is outside the current view, then we might want to hide it... unless it was previously OUTSIDE, + // if it was previously outside, then we can safely assume it's already hidden, and we can also safely assume + // that all of it's children are outside both of our views, in which case we can just stop recursing... + if (args->culledOnce && args->wantDeltaFrustums && inLastCulledFrustum == ViewFrustum::OUTSIDE) { + args->nodesScanned++; + args->nodesOutsideOutside++; + return false; // stop recursing this branch! + } + + // if this node is fully OUTSIDE the view, but previously intersected and/or was inside the last view, then + // we need to hide it. Additionally we know that ALL of it's children are also fully OUTSIDE so we can recurse + // the children and simply mark them as hidden args->tree->recurseNodeWithOperation(node, hideAllSubTreeOperation, args ); return false; } break; case ViewFrustum::INSIDE: { - // if this node is fully INSIDE the view, then we know that ALL of it's children are also fully INSIDE - // so we can recurse the children and simply mark them as visible (as appropriate based on LOD) - args->tree->recurseNodeWithOperation(node, showAllSubTreeOperation, args ); + + // If this node is INSIDE the current view, then we might want to show it... unless it was previously INSIDE, + // if it was previously INSIDE, then we can safely assume it's already shown, and we can also safely assume + // that all of it's children are INSIDE both of our views, in which case we can just stop recursing... + if (args->culledOnce && args->wantDeltaFrustums && inLastCulledFrustum == ViewFrustum::INSIDE) { + args->nodesScanned++; + args->nodesInsideInside++; + return false; // stop recursing this branch! + } + + // if this node is fully INSIDE the view, but previously INTERSECTED and/or was OUTSIDE the last view, then + // we need to show it. Additionally we know that ALL of it's children are also fully INSIDE so we can recurse + // the children and simply mark them as visible (as appropriate based on LOD) + args->tree->recurseNodeWithOperation(node, showAllSubTreeOperation, args); return false; } break; case ViewFrustum::INTERSECT: { args->nodesScanned++; + + // If this node INTERSECTS the current view, then we might want to show it... unless it was previously INSIDE + // the last known view, in which case it will already be visible, and we know that all it's children are also + // previously INSIDE and visible. So in this case stop recursing + if (args->culledOnce && args->wantDeltaFrustums && inLastCulledFrustum == ViewFrustum::INSIDE) { + args->nodesIntersectInside++; + return false; // stop recursing this branch! + } + args->nodesIntersect++; // if the child node INTERSECTs the view, then we want to check to see if it thinks it should render @@ -1886,6 +2124,7 @@ public: }; bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData) { + collectStatsForTreesAndVBOsArgs* args = (collectStatsForTreesAndVBOsArgs*)extraData; args->totalNodes++; diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 238b9f6d96..7428f82013 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -59,6 +59,9 @@ public: void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; } unsigned long getVoxelsUpdated() const { return _voxelsUpdated; } unsigned long getVoxelsRendered() const { return _voxelsInReadArrays; } + unsigned long getVoxelsWritten() const { return _voxelsInWriteArrays; } + + ViewFrustum* getLastCulledViewFrustum() { return &_lastCulledViewFrustum; } void loadVoxelsFile(const char* fileName,bool wantColorRandomizer); void writeToSVOFile(const char* filename, VoxelNode* node) const; @@ -123,6 +126,7 @@ public slots: void collectStatsForTreesAndVBOs(); // Methods that recurse tree + void showAllLocalVoxels(); void randomizeVoxelColors(); void falseColorizeRandom(); void trueColorize(); @@ -192,6 +196,7 @@ private: static bool hideOutOfViewUnrollOperation(VoxelNode* node, void* extraData); static bool hideAllSubTreeOperation(VoxelNode* node, void* extraData); static bool showAllSubTreeOperation(VoxelNode* node, void* extraData); + static bool showAllLocalVoxelsOperation(VoxelNode* node, void* extraData); int updateNodeInArrays(VoxelNode* node, bool reuseIndex, bool forceDraw); int forceRemoveNodeFromArrays(VoxelNode* node); @@ -252,10 +257,13 @@ private: pthread_mutex_t _bufferWriteLock; pthread_mutex_t _treeLock; - ViewFrustum _lastKnowViewFrustum; + ViewFrustum _lastKnownViewFrustum; ViewFrustum _lastStableViewFrustum; ViewFrustum* _viewFrustum; + ViewFrustum _lastCulledViewFrustum; // used for hide/show visible passes + bool _culledOnce; + void setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndices[]); int newTreeToArrays(VoxelNode *currentNode); diff --git a/libraries/shared/src/PerfStat.h b/libraries/shared/src/PerfStat.h index e460548f1d..81e2818fc5 100644 --- a/libraries/shared/src/PerfStat.h +++ b/libraries/shared/src/PerfStat.h @@ -94,7 +94,8 @@ private: static bool _suppressShortTimings; public: - PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false, uint64_t* runningTotal = NULL, uint64_t* totalCalls = NULL) : + PerformanceWarning(bool renderWarnings, const char* message, bool alwaysDisplay = false, + uint64_t* runningTotal = NULL, uint64_t* totalCalls = NULL) : _start(usecTimestampNow()), _message(message), _renderWarningsOn(renderWarnings), diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index e850be7ad8..b4fbce630f 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -79,6 +79,7 @@ void VoxelNode::init(unsigned char * octalCode) { _unknownBufferIndex = true; setBufferIndex(GLBUFFER_INDEX_UNKNOWN); + setVoxelSystem(NULL); _isDirty = true; _shouldRender = false; @@ -104,18 +105,8 @@ VoxelNode::~VoxelNode() { delete[] _octalCode.pointer; } - // delete all of this node's children - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - VoxelNode* childAt = getChildAtIndex(i); - if (childAt) { - delete childAt; - setChildAtIndex(i, NULL); - } - } - - // at this point, we should have no children, but we need to drop ourselves from population counts - _singleChildrenCount--; - _childrenCount[0]--; + // delete all of this node's children, this also takes care of all population tracking data + deleteAllChildren(); } void VoxelNode::markWithChangedTime() { @@ -146,7 +137,9 @@ std::map VoxelNode::_mapIndexToVoxelSystemPointers; VoxelSystem* VoxelNode::getVoxelSystem() const { if (_voxelSystemIndex > INDEX_FOR_NULL) { if (_mapIndexToVoxelSystemPointers.end() != _mapIndexToVoxelSystemPointers.find(_voxelSystemIndex)) { - return _mapIndexToVoxelSystemPointers[_voxelSystemIndex]; + + VoxelSystem* voxelSystem = _mapIndexToVoxelSystemPointers[_voxelSystemIndex]; + return voxelSystem; } } return NULL; @@ -287,7 +280,8 @@ void VoxelNode::auditChildren(const char* label) const { } } - if (auditFailed) { + const bool alwaysReport = false; // set this to true to get additional debugging + if (alwaysReport || auditFailed) { qDebug("%s... auditChildren() %s <<<< \n", label, (auditFailed ? "FAILED" : "PASSED")); qDebug(" _childrenExternal=%s\n", debug::valueOf(_childrenExternal)); qDebug(" childCount=%d\n", getChildCount()); @@ -605,6 +599,60 @@ void VoxelNode::checkStoreFourChildren(VoxelNode* childOne, VoxelNode* childTwo, } } +void VoxelNode::deleteAllChildren() { + // first delete all the VoxelNode objects... + for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { + VoxelNode* childAt = getChildAtIndex(i); + if (childAt) { + delete childAt; + } + } + + // now, reset our internal state and ANY and all population data + int childCount = getChildCount(); + switch (childCount) { + case 0: { + _singleChildrenCount--; + _childrenCount[0]--; + } break; + case 1: { + _singleChildrenCount--; + _childrenCount[1]--; + } break; + + case 2: { + if (_childrenExternal) { + _twoChildrenExternalCount--; + } else { + _twoChildrenOffsetCount--; + } + _childrenCount[2]--; + } break; + + case 3: { + if (_childrenExternal) { + _threeChildrenExternalCount--; + } else { + _threeChildrenOffsetCount--; + } + _childrenCount[3]--; + } break; + + default: { + _externalChildrenCount--; + _childrenCount[childCount]--; + } break; + + + } + + // If we had externally stored children, clean them too. + if (_childrenExternal && _children.external) { + delete[] _children.external; + } + _children.single = NULL; +} + void VoxelNode::setChildAtIndex(int childIndex, VoxelNode* child) { PerformanceWarning warn(false,"setChildAtIndex",false,&_setChildAtIndexTime,&_setChildAtIndexCalls); diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 4f0fa96ecc..f52198efaf 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -9,6 +9,8 @@ #ifndef __hifi__VoxelNode__ #define __hifi__VoxelNode__ +//#define HAS_AUDIT_CHILDREN + #include #include "AABox.h" #include "ViewFrustum.h" @@ -144,6 +146,7 @@ public: #endif // def HAS_AUDIT_CHILDREN private: + void deleteAllChildren(); void setChildAtIndex(int childIndex, VoxelNode* child); void storeTwoChildren(VoxelNode* childOne, VoxelNode* childTwo); void retrieveTwoChildren(VoxelNode*& childOne, VoxelNode*& childTwo); diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index eadba04e65..6c1fe42b76 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -476,7 +476,9 @@ void VoxelTree::deleteVoxelCodeFromTreeRecursion(VoxelNode* node, void* extraDat void VoxelTree::eraseAllVoxels() { // XXXBHG Hack attack - is there a better way to erase the voxel tree? delete rootNode; // this will recurse and delete all children + VoxelSystem* voxelSystem = rootNode->getVoxelSystem(); rootNode = new VoxelNode(); + rootNode->setVoxelSystem(voxelSystem); _isDirty = true; }