diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6c76f589da..c08c5e04fa 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -620,7 +620,13 @@ void Application::keyPressEvent(QKeyEvent* event) { _audioScope.inputPaused = !_audioScope.inputPaused; break; case Qt::Key_L: - _displayLevels = !_displayLevels; + if (!isShifted && !isMeta) { + _displayLevels = !_displayLevels; + } else if (isShifted) { + Menu::getInstance()->triggerOption(MenuOption::LodTools); + } else if (isMeta) { + Menu::getInstance()->triggerOption(MenuOption::Log); + } break; case Qt::Key_E: @@ -2372,6 +2378,8 @@ void Application::queryVoxels() { _voxelQuery.setCameraNearClip(_viewFrustum.getNearClip()); _voxelQuery.setCameraFarClip(_viewFrustum.getFarClip()); _voxelQuery.setCameraEyeOffsetPosition(_viewFrustum.getEyeOffsetPosition()); + _voxelQuery.setVoxelSizeScale(Menu::getInstance()->getVoxelSizeScale()); + _voxelQuery.setBoundaryLevelAdjust(Menu::getInstance()->getBoundaryLevelAdjust()); unsigned char voxelQueryPacket[MAX_PACKET_SIZE]; diff --git a/interface/src/Application.h b/interface/src/Application.h index 531ff1813b..6cc973edac 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -60,6 +60,7 @@ #include "ui/ChatEntry.h" #include "ui/VoxelStatsDialog.h" #include "ui/RearMirrorTools.h" +#include "ui/LodToolsDialog.h" class QAction; class QActionGroup; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index a431b4b457..1c4a697f24 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -53,7 +53,10 @@ Menu::Menu() : _viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET), _voxelModeActionsGroup(NULL), _voxelStatsDialog(NULL), - _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM) + _lodToolsDialog(NULL), + _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), + _voxelSizeScale(DEFAULT_VOXEL_SIZE_SCALE), + _boundaryLevelAdjust(0) { Application *appInstance = Application::getInstance(); @@ -277,6 +280,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion); + addActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools())); QMenu* cullingOptionsMenu = voxelOptionsMenu->addMenu("Culling Options"); addDisabledActionAndSeparator(cullingOptionsMenu, "Standard Settings"); @@ -517,6 +521,8 @@ void Menu::loadSettings(QSettings* settings) { _audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0); _fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES); _maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM); + _voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_VOXEL_SIZE_SCALE); + _boundaryLevelAdjust = loadSetting(settings, "boundaryLevelAdjust", 0); settings->beginGroup("View Frustum Offset Camera"); // in case settings is corrupt or missing loadSetting() will check for NaN @@ -543,6 +549,8 @@ void Menu::saveSettings(QSettings* settings) { settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples); settings->setValue("fieldOfView", _fieldOfView); settings->setValue("maxVoxels", _maxVoxels); + settings->setValue("voxelSizeScale", _voxelSizeScale); + settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust); settings->beginGroup("View Frustum Offset Camera"); settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw); settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffset.pitch); @@ -1022,7 +1030,6 @@ void Menu::goToUser() { } void Menu::bandwidthDetails() { - if (! _bandwidthDialog) { _bandwidthDialog = new BandwidthDialog(Application::getInstance()->getGLWidget(), Application::getInstance()->getBandwidthMeter()); @@ -1057,6 +1064,32 @@ void Menu::voxelStatsDetailsClosed() { } } +void Menu::setVoxelSizeScale(float sizeScale) { + _voxelSizeScale = sizeScale; + Application::getInstance()->getVoxels()->redrawInViewVoxels(); +} + +void Menu::setBoundaryLevelAdjust(int boundaryLevelAdjust) { + _boundaryLevelAdjust = boundaryLevelAdjust; + Application::getInstance()->getVoxels()->redrawInViewVoxels(); +} + +void Menu::lodTools() { + if (!_lodToolsDialog) { + _lodToolsDialog = new LodToolsDialog(Application::getInstance()->getGLWidget()); + connect(_lodToolsDialog, SIGNAL(closed()), SLOT(lodToolsClosed())); + _lodToolsDialog->show(); + } + _lodToolsDialog->raise(); +} + +void Menu::lodToolsClosed() { + if (_lodToolsDialog) { + delete _lodToolsDialog; + _lodToolsDialog = NULL; + } +} + void Menu::cycleFrustumRenderMode() { _frustumDrawMode = (FrustumDrawMode)((_frustumDrawMode + 1) % FRUSTUM_DRAW_MODE_COUNT); updateFrustumRenderModeAction(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 9ba234fcb3..1bb25d8f21 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -35,6 +35,7 @@ class QSettings; class BandwidthDialog; class VoxelStatsDialog; +class LodToolsDialog; class Menu : public QMenuBar { Q_OBJECT @@ -53,15 +54,23 @@ public: FrustumDrawMode getFrustumDrawMode() const { return _frustumDrawMode; } ViewFrustumOffset getViewFrustumOffset() const { return _viewFrustumOffset; } VoxelStatsDialog* getVoxelStatsDialog() const { return _voxelStatsDialog; } + LodToolsDialog* getLodToolsDialog() const { return _lodToolsDialog; } int getMaxVoxels() const { return _maxVoxels; } QAction* getUseVoxelShader() const { return _useVoxelShader; } void handleViewFrustumOffsetKeyModifier(int key); + + // User Tweakable LOD Items + void setVoxelSizeScale(float sizeScale); + float getVoxelSizeScale() const { return _voxelSizeScale; } + void setBoundaryLevelAdjust(int boundaryLevelAdjust); + int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } public slots: void bandwidthDetails(); void voxelStatsDetails(); + void lodTools(); void loadSettings(QSettings* settings = NULL); void saveSettings(QSettings* settings = NULL); void importSettings(); @@ -76,6 +85,7 @@ private slots: void goToLocation(); void bandwidthDetailsClosed(); void voxelStatsDetailsClosed(); + void lodToolsClosed(); void cycleFrustumRenderMode(); void updateVoxelModeActions(); void chooseVoxelPaintColor(); @@ -120,7 +130,10 @@ private: ViewFrustumOffset _viewFrustumOffset; QActionGroup* _voxelModeActionsGroup; VoxelStatsDialog* _voxelStatsDialog; + LodToolsDialog* _lodToolsDialog; int _maxVoxels; + float _voxelSizeScale; + int _boundaryLevelAdjust; QAction* _useVoxelShader; }; @@ -178,6 +191,7 @@ namespace MenuOption { const QString GoHome = "Go Home"; const QString Gravity = "Use Gravity"; const QString ParticleCloud = "Particle Cloud"; + const QString LodTools = "LOD Tools"; const QString Log = "Log"; const QString Login = "Login"; const QString LookAtIndicator = "Look-at Indicator"; diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index b7afd67947..7581dcc8db 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -109,6 +109,7 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) _useFastVoxelPipeline = false; _culledOnce = false; + _inhideOutOfView = false; } void VoxelSystem::voxelDeleted(VoxelNode* node) { @@ -139,7 +140,9 @@ void VoxelSystem::voxelUpdated(VoxelNode* node) { if (node->getVoxelSystem() == this) { bool shouldRender = false; // assume we don't need to render it // if it's colored, we might need to render it! - shouldRender = node->calculateShouldRender(_viewFrustum); + float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale(); + int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); + shouldRender = node->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust); if (node->getShouldRender() != shouldRender) { node->setShouldRender(shouldRender); @@ -155,7 +158,7 @@ void VoxelSystem::voxelUpdated(VoxelNode* node) { VoxelNode* childNode = node->getChildAtIndex(i); if (childNode) { bool wasShouldRender = childNode->getShouldRender(); - bool isShouldRender = childNode->calculateShouldRender(_viewFrustum); + bool isShouldRender = childNode->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust); if (wasShouldRender && !isShouldRender) { childrenGotHiddenCount++; } @@ -923,7 +926,9 @@ 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); + float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale();; + int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); + shouldRender = node->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust); node->setShouldRender(shouldRender); // let children figure out their renderness @@ -1384,6 +1389,10 @@ void VoxelSystem::killLocalVoxels() { setupNewVoxelsForDrawing(); } +void VoxelSystem::redrawInViewVoxels() { + hideOutOfView(true); +} + bool VoxelSystem::clearAllNodesBufferIndexOperation(VoxelNode* node, void* extraData) { _nodeCount++; @@ -1771,7 +1780,9 @@ bool VoxelSystem::showAllLocalVoxelsOperation(VoxelNode* node, void* extraData) args->nodesScanned++; - bool shouldRender = true; // node->calculateShouldRender(&args->thisViewFrustum); + float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale();; + int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); + bool shouldRender = node->calculateShouldRender(&args->thisViewFrustum, voxelSizeScale, boundaryLevelAdjust); node->setShouldRender(shouldRender); if (shouldRender) { @@ -1838,6 +1849,14 @@ public: }; void VoxelSystem::hideOutOfView(bool forceFullFrustum) { + + // don't re-enter... + if (_inhideOutOfView) { + return; + } + + _inhideOutOfView = true; + bool showDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showDebugDetails, "hideOutOfView()", showDebugDetails); bool widenFrustum = true; @@ -1869,10 +1888,13 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { if (!forceFullFrustum && _culledOnce && args.lastViewFrustum.isVerySimilar(args.thisViewFrustum)) { //printf("view frustum hasn't changed BAIL!!!\n"); + _inhideOutOfView = false; return; } - + + pthread_mutex_lock(&_treeLock); _tree->recurseTreeWithOperation(hideOutOfViewOperation,(void*)&args); + pthread_mutex_unlock(&_treeLock); _lastCulledViewFrustum = args.thisViewFrustum; // save last stable _culledOnce = true; @@ -1890,6 +1912,7 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) { args.nodesInsideInside, args.nodesIntersectInside, args.nodesOutsideOutside ); } + _inhideOutOfView = false; } bool VoxelSystem::hideAllSubTreeOperation(VoxelNode* node, void* extraData) { @@ -1947,7 +1970,9 @@ bool VoxelSystem::showAllSubTreeOperation(VoxelNode* node, void* extraData) { args->nodesInside++; - bool shouldRender = node->calculateShouldRender(&args->thisViewFrustum); + float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale(); + int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); + bool shouldRender = node->calculateShouldRender(&args->thisViewFrustum, voxelSizeScale, boundaryLevelAdjust); node->setShouldRender(shouldRender); if (shouldRender && !node->isKnownBufferIndex()) { diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index d5434ec7f7..f99e487a07 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -83,6 +83,7 @@ public: float getVoxelsBytesReadPerSecondAverage(); void killLocalVoxels(); + void redrawInViewVoxels(); virtual void removeOutOfView(); virtual void hideOutOfView(bool forceFullFrustum = false); @@ -298,6 +299,8 @@ private: bool _inSetupNewVoxelsForDrawing; bool _useFastVoxelPipeline; + + bool _inhideOutOfView; }; #endif diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp new file mode 100644 index 0000000000..44cd66d0b6 --- /dev/null +++ b/interface/src/ui/LodToolsDialog.cpp @@ -0,0 +1,153 @@ +// +// LodToolsDialog.cpp +// interface +// +// Created by Brad Hefta-Gaub on 7/19/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "Menu.h" +#include "ui/LodToolsDialog.h" + + +LodToolsDialog::LodToolsDialog(QWidget* parent) : + QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint) +{ + this->setWindowTitle("LOD Tools"); + + // Create layouter + QFormLayout* form = new QFormLayout(); + + _lodSize = new QSlider(Qt::Horizontal); + const int MAX_LOD_SIZE = MAX_LOD_SIZE_MULTIPLIER; + const int MIN_LOD_SIZE = 0; + const int STEP_LOD_SIZE = 1; + const int PAGE_STEP_LOD_SIZE = 100; + const int SLIDER_WIDTH = 300; + _lodSize->setMaximum(MAX_LOD_SIZE); + _lodSize->setMinimum(MIN_LOD_SIZE); + _lodSize->setSingleStep(STEP_LOD_SIZE); + _lodSize->setTickInterval(PAGE_STEP_LOD_SIZE); + _lodSize->setTickPosition(QSlider::TicksBelow); + _lodSize->setFixedWidth(SLIDER_WIDTH); + _lodSize->setPageStep(PAGE_STEP_LOD_SIZE); + int sliderValue = Menu::getInstance()->getVoxelSizeScale() / TREE_SCALE; + _lodSize->setValue(sliderValue); + form->addRow("LOD Size Scale:", _lodSize); + connect(_lodSize,SIGNAL(valueChanged(int)),this,SLOT(sizeScaleValueChanged(int))); + + _boundaryLevelAdjust = new QSlider(Qt::Horizontal); + const int MAX_ADJUST = 10; + const int MIN_ADJUST = 0; + const int STEP_ADJUST = 1; + _boundaryLevelAdjust->setMaximum(MAX_ADJUST); + _boundaryLevelAdjust->setMinimum(MIN_ADJUST); + _boundaryLevelAdjust->setSingleStep(STEP_ADJUST); + _boundaryLevelAdjust->setTickInterval(STEP_ADJUST); + _boundaryLevelAdjust->setTickPosition(QSlider::TicksBelow); + _boundaryLevelAdjust->setFixedWidth(SLIDER_WIDTH); + sliderValue = Menu::getInstance()->getBoundaryLevelAdjust(); + _boundaryLevelAdjust->setValue(sliderValue); + form->addRow("Boundary Level Adjust:", _boundaryLevelAdjust); + connect(_boundaryLevelAdjust,SIGNAL(valueChanged(int)),this,SLOT(boundaryLevelValueChanged(int))); + + // Create a label with feedback... + _feedback = new QLabel(); + QPalette palette = _feedback->palette(); + const unsigned redish = 0xfff00000; + palette.setColor(QPalette::WindowText, QColor::fromRgb(redish)); + _feedback->setPalette(palette); + _feedback->setText(getFeedbackText()); + const int FEEDBACK_WIDTH = 350; + _feedback->setFixedWidth(FEEDBACK_WIDTH); + form->addRow("You can see... ", _feedback); + + // Add a button to reset + QPushButton* resetButton = new QPushButton("Reset"); + form->addRow("", resetButton); + connect(resetButton,SIGNAL(clicked(bool)),this,SLOT(resetClicked(bool))); + + this->QDialog::setLayout(form); +} + +QString LodToolsDialog::getFeedbackText() { + // determine granularity feedback + int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); + QString granularityFeedback; + + switch (boundaryLevelAdjust) { + case 0: { + granularityFeedback = QString("at standard granularity."); + } break; + case 1: { + granularityFeedback = QString("at half of standard granularity."); + } break; + case 2: { + granularityFeedback = QString("at a third of standard granularity."); + } break; + default: { + granularityFeedback = QString("at 1/%1th of standard granularity.").arg(boundaryLevelAdjust + 1); + } break; + } + + // distance feedback + float voxelSizeScale = Menu::getInstance()->getVoxelSizeScale(); + float relativeToDefault = voxelSizeScale / DEFAULT_VOXEL_SIZE_SCALE; + QString result; + if (relativeToDefault > 1.01) { + result = QString("%1 further %2").arg(relativeToDefault,8,'f',2).arg(granularityFeedback); + } else if (relativeToDefault > 0.99) { + result = QString("the default distance %1").arg(granularityFeedback); + } else { + result = QString("%1 of default %2").arg(relativeToDefault,8,'f',3).arg(granularityFeedback); + } + return result; +} + +LodToolsDialog::~LodToolsDialog() { + delete _feedback; + delete _lodSize; + delete _boundaryLevelAdjust; +} + +void LodToolsDialog::sizeScaleValueChanged(int value) { + float realValue = value * TREE_SCALE; + Menu::getInstance()->setVoxelSizeScale(realValue); + + _feedback->setText(getFeedbackText()); +} + +void LodToolsDialog::boundaryLevelValueChanged(int value) { + Menu::getInstance()->setBoundaryLevelAdjust(value); + _feedback->setText(getFeedbackText()); +} + +void LodToolsDialog::resetClicked(bool checked) { + int sliderValue = DEFAULT_VOXEL_SIZE_SCALE / TREE_SCALE; + //sizeScaleValueChanged(sliderValue); + _lodSize->setValue(sliderValue); + _boundaryLevelAdjust->setValue(0); +} + +void LodToolsDialog::reject() { + // Just regularly close upon ESC + this->QDialog::close(); +} + +void LodToolsDialog::closeEvent(QCloseEvent* event) { + this->QDialog::closeEvent(event); + emit closed(); +} + + diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h new file mode 100644 index 0000000000..fad9749776 --- /dev/null +++ b/interface/src/ui/LodToolsDialog.h @@ -0,0 +1,48 @@ +// +// LodToolsDialog.h +// interface +// +// Created by Brad Hefta-Gaub on 7/19/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__LodToolsDialog__ +#define __hifi__LodToolsDialog__ + +#include +#include +#include + +#include + +class LodToolsDialog : public QDialog { + Q_OBJECT +public: + // Sets up the UI + LodToolsDialog(QWidget* parent); + ~LodToolsDialog(); + +signals: + void closed(); + +public slots: + void reject(); + void sizeScaleValueChanged(int value); + void boundaryLevelValueChanged(int value); + void resetClicked(bool checked); + +protected: + + // Emits a 'closed' signal when this dialog is closed. + void closeEvent(QCloseEvent*); + +private: + QString getFeedbackText(); + + QSlider* _lodSize; + QSlider* _boundaryLevelAdjust; + QLabel* _feedback; +}; + +#endif /* defined(__interface__LodToolsDialog__) */ + diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 70544f536b..dc953b6b80 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -35,7 +35,10 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { case PACKET_TYPE_DOMAIN_LIST_REQUEST: case PACKET_TYPE_DOMAIN_REPORT_FOR_DUTY: return 1; - + + case PACKET_TYPE_VOXEL_QUERY: + return 1; + default: return 0; } diff --git a/libraries/voxel-server-library/src/VoxelNodeData.cpp b/libraries/voxel-server-library/src/VoxelNodeData.cpp index 705ea8b2e7..ced0669283 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.cpp +++ b/libraries/voxel-server-library/src/VoxelNodeData.cpp @@ -23,7 +23,11 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) : _viewFrustumChanging(false), _viewFrustumJustStoppedChanging(true), _currentPacketIsColor(true), - _voxelSendThread(NULL) + _voxelSendThread(NULL), + _lastClientBoundaryLevelAdjust(0), + _lastClientVoxelSizeScale(DEFAULT_VOXEL_SIZE_SCALE), + _lodChanged(false), + _lodInitialized(false) { _voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE]; _voxelPacketAt = _voxelPacket; @@ -140,6 +144,23 @@ bool VoxelNodeData::updateCurrentViewFrustum() { currentViewFrustumChanged = true; } + // Also check for LOD changes from the client + if (_lodInitialized) { + if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) { + _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust(); + _lodChanged = true; + } + if (_lastClientVoxelSizeScale != getVoxelSizeScale()) { + _lastClientVoxelSizeScale = getVoxelSizeScale(); + _lodChanged = true; + } + } else { + _lodInitialized = true; + _lastClientVoxelSizeScale = getVoxelSizeScale(); + _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust(); + _lodChanged = false; + } + // When we first detect that the view stopped changing, we record this. // but we don't change it back to false until we've completely sent this // scene. @@ -154,6 +175,7 @@ void VoxelNodeData::setViewSent(bool viewSent) { _viewSent = viewSent; if (viewSent) { _viewFrustumJustStoppedChanging = false; + _lodChanged = false; } } diff --git a/libraries/voxel-server-library/src/VoxelNodeData.h b/libraries/voxel-server-library/src/VoxelNodeData.h index 4049cdda36..9f871b72ab 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.h +++ b/libraries/voxel-server-library/src/VoxelNodeData.h @@ -48,25 +48,27 @@ public: VoxelNodeBag nodeBag; CoverageMap map; - ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }; - ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; }; + ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }; + ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; }; // These are not classic setters because they are calculating and maintaining state // which is set asynchronously through the network receive bool updateCurrentViewFrustum(); void updateLastKnownViewFrustum(); - bool getViewSent() const { return _viewSent; }; + bool getViewSent() const { return _viewSent; }; void setViewSent(bool viewSent); - bool getViewFrustumChanging() const { return _viewFrustumChanging; }; + bool getViewFrustumChanging() const { return _viewFrustumChanging; }; bool getViewFrustumJustStoppedChanging() const { return _viewFrustumJustStoppedChanging; }; - uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }; - void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; }; + uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }; + void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; }; bool getCurrentPacketIsColor() const { return _currentPacketIsColor; }; + + bool hasLodChanged() const { return _lodChanged; }; VoxelSceneStats stats; @@ -98,6 +100,12 @@ private: bool _currentPacketIsColor; VoxelSendThread* _voxelSendThread; + + // watch for LOD changes + int _lastClientBoundaryLevelAdjust; + float _lastClientVoxelSizeScale; + bool _lodChanged; + bool _lodInitialized; }; #endif /* defined(__hifi__VoxelNodeData__) */ diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp index 3a3904e218..fcb6e19955 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -175,7 +175,7 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no // if our view has changed, we need to reset these things... if (viewFrustumChanged) { - if (_myServer->wantDumpVoxelsOnMove()) { + if (_myServer->wantDumpVoxelsOnMove() || nodeData->hasLodChanged()) { nodeData->nodeBag.deleteAll(); } nodeData->map.erase(); @@ -194,7 +194,8 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no } // start tracking our stats - bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging(); + bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) + && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); // If we're starting a full scene, then definitely we want to empty the nodeBag if (isFullScene) { @@ -250,15 +251,20 @@ void VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* no VoxelNode* subTree = nodeData->nodeBag.extract(); bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; - int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving() - ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST; - bool isFullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && - nodeData->getViewFrustumJustStoppedChanging(); + float voxelSizeScale = nodeData->getVoxelSizeScale(); + int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); + + int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() + ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); + + + bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) && + nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, - wantOcclusionCulling, coverageMap, boundaryLevelAdjust, + wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, nodeData->getLastTimeBagEmpty(), isFullScene, &nodeData->stats, _myServer->getJurisdiction()); diff --git a/libraries/voxels/src/VoxelConstants.h b/libraries/voxels/src/VoxelConstants.h index e6773ceaa0..e0cd514214 100644 --- a/libraries/voxels/src/VoxelConstants.h +++ b/libraries/voxels/src/VoxelConstants.h @@ -13,6 +13,7 @@ #define __hifi_VoxelConstants_h__ #include +#include #include #include @@ -27,7 +28,8 @@ const uint64_t CHANGE_FUDGE = 1000 * 200; // useconds of fudge in determining if const int TREE_SCALE = 16384; // ~10 miles.. This is the number of meters of the 0.0 to 1.0 voxel universe // This controls the LOD. Larger number will make smaller voxels visible at greater distance. -const float VOXEL_SIZE_SCALE = TREE_SCALE * 400.0f; +const float DEFAULT_VOXEL_SIZE_SCALE = TREE_SCALE * 400.0f; +const float MAX_LOD_SIZE_MULTIPLIER = 2000.0f; const int NUMBER_OF_CHILDREN = 8; const int MAX_VOXEL_PACKET_SIZE = 1492; diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index 4ba90c87c0..d10b02b193 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -1371,12 +1371,12 @@ ViewFrustum::location VoxelNode::inFrustum(const ViewFrustum& viewFrustum) const // Since, if we know the camera position and orientation, we can know which of the corners is the "furthest" // corner. We can use we can use this corner as our "voxel position" to do our distance calculations off of. // By doing this, we don't need to test each child voxel's position vs the LOD boundary -bool VoxelNode::calculateShouldRender(const ViewFrustum* viewFrustum, int boundaryLevelAdjust) const { +bool VoxelNode::calculateShouldRender(const ViewFrustum* viewFrustum, float voxelScaleSize, int boundaryLevelAdjust) const { bool shouldRender = false; if (isColored()) { float furthestDistance = furthestDistanceToCamera(*viewFrustum); - float boundary = boundaryDistanceForRenderLevel(getLevel() + boundaryLevelAdjust); - float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust); + float boundary = boundaryDistanceForRenderLevel(getLevel() + boundaryLevelAdjust, voxelScaleSize); + float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize); bool inBoundary = (furthestDistance <= boundary); bool inChildBoundary = (furthestDistance <= childBoundary); shouldRender = (isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary); diff --git a/libraries/voxels/src/VoxelNode.h b/libraries/voxels/src/VoxelNode.h index 17760f0514..53af7ab602 100644 --- a/libraries/voxels/src/VoxelNode.h +++ b/libraries/voxels/src/VoxelNode.h @@ -69,7 +69,8 @@ public: float distanceToCamera(const ViewFrustum& viewFrustum) const; float furthestDistanceToCamera(const ViewFrustum& viewFrustum) const; - bool calculateShouldRender(const ViewFrustum* viewFrustum, int boundaryLevelAdjust = 0) const; + bool calculateShouldRender(const ViewFrustum* viewFrustum, + float voxelSizeScale = DEFAULT_VOXEL_SIZE_SCALE, int boundaryLevelAdjust = 0) const; // points are assumed to be in Voxel Coordinates (not TREE_SCALE'd) float distanceSquareToPoint(const glm::vec3& point) const; // when you don't need the actual distance, use this. diff --git a/libraries/voxels/src/VoxelQuery.cpp b/libraries/voxels/src/VoxelQuery.cpp index 507c086619..463b806eba 100644 --- a/libraries/voxels/src/VoxelQuery.cpp +++ b/libraries/voxels/src/VoxelQuery.cpp @@ -35,7 +35,8 @@ VoxelQuery::VoxelQuery(Node* owningNode) : _wantDelta(true), _wantLowResMoving(true), _wantOcclusionCulling(true), - _maxVoxelPPS(DEFAULT_MAX_VOXEL_PPS) + _maxVoxelPPS(DEFAULT_MAX_VOXEL_PPS), + _voxelSizeScale(DEFAULT_VOXEL_SIZE_SCALE) { } @@ -78,6 +79,14 @@ int VoxelQuery::getBroadcastData(unsigned char* destinationBuffer) { // desired Max Voxel PPS memcpy(destinationBuffer, &_maxVoxelPPS, sizeof(_maxVoxelPPS)); destinationBuffer += sizeof(_maxVoxelPPS); + + // desired voxelSizeScale + memcpy(destinationBuffer, &_voxelSizeScale, sizeof(_voxelSizeScale)); + destinationBuffer += sizeof(_voxelSizeScale); + + // desired boundaryLevelAdjust + memcpy(destinationBuffer, &_boundaryLevelAdjust, sizeof(_boundaryLevelAdjust)); + destinationBuffer += sizeof(_boundaryLevelAdjust); return destinationBuffer - bufferStart; } @@ -120,7 +129,15 @@ int VoxelQuery::parseData(unsigned char* sourceBuffer, int numBytes) { // desired Max Voxel PPS memcpy(&_maxVoxelPPS, sourceBuffer, sizeof(_maxVoxelPPS)); sourceBuffer += sizeof(_maxVoxelPPS); - + + // desired voxelSizeScale + memcpy(&_voxelSizeScale, sourceBuffer, sizeof(_voxelSizeScale)); + sourceBuffer += sizeof(_voxelSizeScale); + + // desired boundaryLevelAdjust + memcpy(&_boundaryLevelAdjust, sourceBuffer, sizeof(_boundaryLevelAdjust)); + sourceBuffer += sizeof(_boundaryLevelAdjust); + return sourceBuffer - startPosition; } diff --git a/libraries/voxels/src/VoxelQuery.h b/libraries/voxels/src/VoxelQuery.h index 1573e54061..bedbfdac4e 100644 --- a/libraries/voxels/src/VoxelQuery.h +++ b/libraries/voxels/src/VoxelQuery.h @@ -69,6 +69,8 @@ public: bool getWantLowResMoving() const { return _wantLowResMoving; } bool getWantOcclusionCulling() const { return _wantOcclusionCulling; } int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPPS; } + float getVoxelSizeScale() const { return _voxelSizeScale; } + int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } public slots: void setWantLowResMoving(bool wantLowResMoving) { _wantLowResMoving = wantLowResMoving; } @@ -76,6 +78,8 @@ public slots: void setWantDelta(bool wantDelta) { _wantDelta = wantDelta; } void setWantOcclusionCulling(bool wantOcclusionCulling) { _wantOcclusionCulling = wantOcclusionCulling; } void setMaxVoxelPacketsPerSecond(int maxVoxelPPS) { _maxVoxelPPS = maxVoxelPPS; } + void setVoxelSizeScale(float voxelSizeScale) { _voxelSizeScale = voxelSizeScale; } + void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; } protected: QUuid _uuid; @@ -95,6 +99,8 @@ protected: bool _wantLowResMoving; bool _wantOcclusionCulling; int _maxVoxelPPS; + float _voxelSizeScale; /// used for LOD calculations + int _boundaryLevelAdjust; /// used for LOD calculations private: // privatize the copy constructor and assignment operator so they cannot be called diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index ac14f0dc58..aca539f9d3 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -33,13 +33,8 @@ #include "VoxelTree.h" #include -float boundaryDistanceForRenderLevel(unsigned int renderLevel) { - return ::VOXEL_SIZE_SCALE / powf(2, renderLevel); -} - -float boundaryDistanceSquaredForRenderLevel(unsigned int renderLevel) { - const float voxelSizeScale = (::VOXEL_SIZE_SCALE/TREE_SCALE) * (::VOXEL_SIZE_SCALE/TREE_SCALE); - return voxelSizeScale / powf(2, (2 * renderLevel)); +float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale) { + return voxelSizeScale / powf(2, renderLevel); } VoxelTree::VoxelTree(bool shouldReaverage) : @@ -1114,7 +1109,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // caller can pass NULL as viewFrustum if they want everything if (params.viewFrustum) { float distance = node->distanceToCamera(*params.viewFrustum); - float boundaryDistance = boundaryDistanceForRenderLevel(node->getLevel() + params.boundaryLevelAdjust); + float boundaryDistance = boundaryDistanceForRenderLevel(node->getLevel() + params.boundaryLevelAdjust, + params.voxelSizeScale); // If we're too far away for our render level, then just return if (distance >= boundaryDistance) { @@ -1288,7 +1284,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // Before we determine consider this further, let's see if it's in our LOD scope... float distance = distancesToChildren[i]; // params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0; float boundaryDistance = !params.viewFrustum ? 1 : - boundaryDistanceForRenderLevel(childNode->getLevel() + params.boundaryLevelAdjust); + boundaryDistanceForRenderLevel(childNode->getLevel() + params.boundaryLevelAdjust, + params.voxelSizeScale); if (!(distance < boundaryDistance)) { // don't need to check childNode here, because we can't get here with no childNode @@ -1341,7 +1338,8 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp bool shouldRender = !params.viewFrustum ? true - : childNode->calculateShouldRender(params.viewFrustum, params.boundaryLevelAdjust); + : childNode->calculateShouldRender(params.viewFrustum, + params.voxelSizeScale, params.boundaryLevelAdjust); // track some stats if (params.stats) { diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 465c1a54aa..933a1f436e 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -47,52 +47,55 @@ const uint64_t IGNORE_LAST_SENT = 0; class EncodeBitstreamParams { public: - int maxEncodeLevel; - int maxLevelReached; - const ViewFrustum* viewFrustum; - bool includeColor; - bool includeExistsBits; - int chopLevels; - bool deltaViewFrustum; - const ViewFrustum* lastViewFrustum; - bool wantOcclusionCulling; - int boundaryLevelAdjust; - uint64_t lastViewFrustumSent; - bool forceSendScene; - VoxelSceneStats* stats; - CoverageMap* map; - JurisdictionMap* jurisdictionMap; + int maxEncodeLevel; + int maxLevelReached; + const ViewFrustum* viewFrustum; + bool includeColor; + bool includeExistsBits; + int chopLevels; + bool deltaViewFrustum; + const ViewFrustum* lastViewFrustum; + bool wantOcclusionCulling; + int boundaryLevelAdjust; + float voxelSizeScale; + uint64_t lastViewFrustumSent; + bool forceSendScene; + VoxelSceneStats* stats; + CoverageMap* map; + JurisdictionMap* jurisdictionMap; EncodeBitstreamParams( - int maxEncodeLevel = INT_MAX, - const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM, - bool includeColor = WANT_COLOR, - bool includeExistsBits = WANT_EXISTS_BITS, - int chopLevels = 0, - bool deltaViewFrustum = false, - const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM, - bool wantOcclusionCulling= NO_OCCLUSION_CULLING, - CoverageMap* map = IGNORE_COVERAGE_MAP, - int boundaryLevelAdjust = NO_BOUNDARY_ADJUST, - uint64_t lastViewFrustumSent = IGNORE_LAST_SENT, - bool forceSendScene = true, - VoxelSceneStats* stats = IGNORE_SCENE_STATS, - JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP) : - maxEncodeLevel (maxEncodeLevel), - maxLevelReached (0), - viewFrustum (viewFrustum), - includeColor (includeColor), - includeExistsBits (includeExistsBits), - chopLevels (chopLevels), - deltaViewFrustum (deltaViewFrustum), - lastViewFrustum (lastViewFrustum), - wantOcclusionCulling (wantOcclusionCulling), - boundaryLevelAdjust (boundaryLevelAdjust), - lastViewFrustumSent (lastViewFrustumSent), - forceSendScene (forceSendScene), - stats (stats), - map (map), - jurisdictionMap (jurisdictionMap) + int maxEncodeLevel = INT_MAX, + const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM, + bool includeColor = WANT_COLOR, + bool includeExistsBits = WANT_EXISTS_BITS, + int chopLevels = 0, + bool deltaViewFrustum = false, + const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM, + bool wantOcclusionCulling = NO_OCCLUSION_CULLING, + CoverageMap* map = IGNORE_COVERAGE_MAP, + int boundaryLevelAdjust = NO_BOUNDARY_ADJUST, + float voxelSizeScale = DEFAULT_VOXEL_SIZE_SCALE, + uint64_t lastViewFrustumSent = IGNORE_LAST_SENT, + bool forceSendScene = true, + VoxelSceneStats* stats = IGNORE_SCENE_STATS, + JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP) : + maxEncodeLevel(maxEncodeLevel), + maxLevelReached(0), + viewFrustum(viewFrustum), + includeColor(includeColor), + includeExistsBits(includeExistsBits), + chopLevels(chopLevels), + deltaViewFrustum(deltaViewFrustum), + lastViewFrustum(lastViewFrustum), + wantOcclusionCulling(wantOcclusionCulling), + boundaryLevelAdjust(boundaryLevelAdjust), + voxelSizeScale(voxelSizeScale), + lastViewFrustumSent(lastViewFrustumSent), + forceSendScene(forceSendScene), + stats(stats), + map(map), + jurisdictionMap(jurisdictionMap) {} }; @@ -265,7 +268,6 @@ private: void chunkifyLeaf(VoxelNode* node); }; -float boundaryDistanceForRenderLevel(unsigned int renderLevel); -float boundaryDistanceSquaredForRenderLevel(unsigned int renderLevel); +float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale); #endif /* defined(__hifi__VoxelTree__) */