diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ac0ba4b704..e5a359e5bd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -23,17 +23,11 @@ #include #include -#include #include -#include #include -#include #include -#include -#include #include #include -#include #include #include #include @@ -62,6 +56,7 @@ #include "InterfaceConfig.h" #include "LogDisplay.h" #include "LeapManager.h" +#include "Menu.h" #include "OculusManager.h" #include "Util.h" #include "renderer/ProgramObject.h" @@ -90,82 +85,6 @@ const int STARTUP_JITTER_SAMPLES = PACKET_LENGTH_SAMPLES_PER_CHANNEL / 2; // Startup optimistically with small jitter buffer that // will start playback on the second received audio packet. -// customized canvas that simply forwards requests/events to the singleton application -class GLCanvas : public QGLWidget { -protected: - - virtual void initializeGL(); - virtual void paintGL(); - virtual void resizeGL(int width, int height); - - virtual void keyPressEvent(QKeyEvent* event); - virtual void keyReleaseEvent(QKeyEvent* event); - - virtual void mouseMoveEvent(QMouseEvent* event); - virtual void mousePressEvent(QMouseEvent* event); - virtual void mouseReleaseEvent(QMouseEvent* event); - - virtual bool event(QEvent* event); - - virtual void wheelEvent(QWheelEvent* event); -}; - -void GLCanvas::initializeGL() { - Application::getInstance()->initializeGL(); - setAttribute(Qt::WA_AcceptTouchEvents); -} - -void GLCanvas::paintGL() { - Application::getInstance()->paintGL(); -} - -void GLCanvas::resizeGL(int width, int height) { - Application::getInstance()->resizeGL(width, height); -} - -void GLCanvas::keyPressEvent(QKeyEvent* event) { - Application::getInstance()->keyPressEvent(event); -} - -void GLCanvas::keyReleaseEvent(QKeyEvent* event) { - Application::getInstance()->keyReleaseEvent(event); -} - -void GLCanvas::mouseMoveEvent(QMouseEvent* event) { - Application::getInstance()->mouseMoveEvent(event); -} - -void GLCanvas::mousePressEvent(QMouseEvent* event) { - Application::getInstance()->mousePressEvent(event); -} - -void GLCanvas::mouseReleaseEvent(QMouseEvent* event) { - Application::getInstance()->mouseReleaseEvent(event); -} - -int updateTime = 0; -bool GLCanvas::event(QEvent* event) { - switch (event->type()) { - case QEvent::TouchBegin: - Application::getInstance()->touchBeginEvent(static_cast(event)); - event->accept(); - return true; - case QEvent::TouchEnd: - Application::getInstance()->touchEndEvent(static_cast(event)); - return true; - case QEvent::TouchUpdate: - Application::getInstance()->touchUpdateEvent(static_cast(event)); - return true; - default: - break; - } - return QGLWidget::event(event); -} - -void GLCanvas::wheelEvent(QWheelEvent* event) { - Application::getInstance()->wheelEvent(event); -} - void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); LogDisplay::instance.addMessage(message.toLocal8Bit().constData()); @@ -175,7 +94,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : QApplication(argc, argv), _window(new QMainWindow(desktop())), _glWidget(new GLCanvas()), - _bandwidthDialog(NULL), _voxelStatsDialog(NULL), _displayLevels(false), _frameCount(0), @@ -184,12 +102,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _particleSystemInitialized(false), _coolDemoParticleEmitter(-1), _wantToKillLocalVoxels(false), - _frustumDrawingMode(FRUSTUM_DRAW_MODE_ALL), - _viewFrustumOffsetYaw(-135.0), - _viewFrustumOffsetPitch(0.0), - _viewFrustumOffsetRoll(0.0), - _viewFrustumOffsetDistance(25.0), - _viewFrustumOffsetUp(0.0), _audioScope(256, 200, true), _mouseX(0), _mouseY(0), @@ -447,14 +359,16 @@ void Application::paintGL() { // myCamera is. But we also want to do meaningful camera transforms on OpenGL for the offset camera Camera whichCamera = _myCamera; - if (_frustumOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { + + ViewFrustumOffset viewFrustumOffset = Menu::getInstance()->getViewFrustumOffset(); // set the camera to third-person view but offset so we can see the frustum _viewFrustumOffsetCamera.setTargetPosition(_myCamera.getTargetPosition()); _viewFrustumOffsetCamera.setTargetRotation(_myCamera.getTargetRotation() * glm::quat(glm::radians(glm::vec3( - _viewFrustumOffsetPitch, _viewFrustumOffsetYaw, _viewFrustumOffsetRoll)))); - _viewFrustumOffsetCamera.setUpShift (_viewFrustumOffsetUp ); - _viewFrustumOffsetCamera.setDistance (_viewFrustumOffsetDistance); + viewFrustumOffset.pitch, viewFrustumOffset.yaw, viewFrustumOffset.roll)))); + _viewFrustumOffsetCamera.setUpShift(viewFrustumOffset.up); + _viewFrustumOffsetCamera.setDistance(viewFrustumOffset.distance); _viewFrustumOffsetCamera.initialize(); // force immediate snap to ideal position and orientation _viewFrustumOffsetCamera.update(1.f/_fps); whichCamera = _viewFrustumOffsetCamera; @@ -484,7 +398,7 @@ void Application::resetCamerasOnResizeGL(Camera& camera, int width, int height) camera.setFieldOfView(2 * atan((0.0468 * _oculusDistortionScale) / 0.041) * (180 / PIf)); } else { camera.setAspectRatio(aspectRatio); - camera.setFieldOfView(_fieldOfView); + camera.setFieldOfView(Menu::getInstance()->getFieldOfView()); } } @@ -514,7 +428,7 @@ void Application::resizeGL(int width, int height) { // If we're in Display Frustum mode, then we want to use the slightly adjust near/far clip values of the // _viewFrustumOffsetCamera, so that we can see more of the application content in the application's frustum - if (_frustumOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { nearVal = _viewFrustumOffsetCamera.getNearClip(); farVal = _viewFrustumOffsetCamera.getFarClip(); } @@ -530,7 +444,7 @@ void Application::controlledBroadcastToNodes(unsigned char* broadcastData, size_ for (int i = 0; i < numNodeTypes; ++i) { // Intercept data to voxel server when voxels are disabled - if (nodeTypes[i] == NODE_TYPE_VOXEL_SERVER && ! self->_renderVoxels->isChecked()) { + if (nodeTypes[i] == NODE_TYPE_VOXEL_SERVER && !Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { continue; } @@ -579,53 +493,25 @@ void Application::keyPressEvent(QKeyEvent* event) { } //this is for switching between modes for the leap rave glove test - if (_simulateLeapHand->isChecked() || _testRaveGlove->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::SimulateLeapHand) + || Menu::getInstance()->isOptionChecked(MenuOption::TestRaveGlove)) { _myAvatar.getHand().setRaveGloveEffectsMode((QKeyEvent*)event); } - bool isMeta = event->modifiers().testFlag(Qt::MetaModifier); bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier); switch (event->key()) { case Qt::Key_BracketLeft: - _viewFrustumOffsetYaw -= 0.5; - break; - case Qt::Key_BracketRight: - _viewFrustumOffsetYaw += 0.5; - break; - case Qt::Key_BraceLeft: - _viewFrustumOffsetPitch -= 0.5; - break; - case Qt::Key_BraceRight: - _viewFrustumOffsetPitch += 0.5; - break; - case Qt::Key_ParenLeft: - _viewFrustumOffsetRoll -= 0.5; - break; - case Qt::Key_ParenRight: - _viewFrustumOffsetRoll += 0.5; - break; - case Qt::Key_Less: - _viewFrustumOffsetDistance -= 0.5; - break; - case Qt::Key_Greater: - _viewFrustumOffsetDistance += 0.5; - break; - case Qt::Key_Comma: - _viewFrustumOffsetUp -= 0.05; - break; - case Qt::Key_Period: - _viewFrustumOffsetUp += 0.05; + Menu::getInstance()->handleViewFrustumOffsetKeyModifier(event->key()); break; - case Qt::Key_Ampersand: _paintOn = !_paintOn; setupPaintingVoxel(); @@ -658,9 +544,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_C: if (isShifted) { - _occlusionCulling->trigger(); - } else if (isMeta) { - chooseVoxelPaintColor(); + Menu::getInstance()->triggerOption(MenuOption::DisableOcclusionCulling); } else { _myAvatar.setDriveKeys(DOWN, 1); } @@ -685,15 +569,15 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_G: if (isShifted) { - _gravityUse->trigger(); + Menu::getInstance()->triggerOption(MenuOption::Gravity); } else { - _eyedropperMode->trigger(); + Menu::getInstance()->triggerOption(MenuOption::VoxelGetColorMode); } break; case Qt::Key_A: if (isShifted) { - _renderAtmosphereOn->trigger(); + Menu::getInstance()->triggerOption(MenuOption::Atmosphere); } else { _myAvatar.setDriveKeys(ROT_LEFT, 1); } @@ -787,47 +671,47 @@ void Application::keyPressEvent(QKeyEvent* event) { resizeGL(_glWidget->width(), _glWidget->height()); break; case Qt::Key_N: - _noise->trigger(); + Menu::getInstance()->triggerOption(MenuOption::Noise); break; case Qt::Key_H: - _lookingInMirror->trigger(); + Menu::getInstance()->triggerOption(MenuOption::Mirror); break; case Qt::Key_F: if (isShifted) { - _frustumOn->trigger(); + Menu::getInstance()->triggerOption(MenuOption::DisplayFrustum); } else { - _fullScreenMode->trigger(); + Menu::getInstance()->triggerOption(MenuOption::Fullscreen); } break; case Qt::Key_V: if (isShifted) { - _renderVoxels->trigger(); + Menu::getInstance()->triggerOption(MenuOption::Voxels); } else { - _addVoxelMode->trigger(); + Menu::getInstance()->triggerOption(MenuOption::VoxelAddMode); } break; case Qt::Key_P: - _manualFirstPerson->trigger(); + Menu::getInstance()->triggerOption(MenuOption::FirstPerson); break; case Qt::Key_R: if (isShifted) { - _frustumRenderModeAction->trigger(); + Menu::getInstance()->triggerOption(MenuOption::FrustumRenderMode); } else { - _deleteVoxelMode->trigger(); + Menu::getInstance()->triggerOption(MenuOption::VoxelDeleteMode); } break; case Qt::Key_B: - _colorVoxelMode->trigger(); + Menu::getInstance()->triggerOption(MenuOption::VoxelColorMode); break; case Qt::Key_O: - _selectVoxelMode->trigger(); + Menu::getInstance()->triggerOption(MenuOption::VoxelSelectMode); break; case Qt::Key_Slash: - _renderStatsOn->trigger(); + Menu::getInstance()->triggerOption(MenuOption::Stats); break; case Qt::Key_Backspace: case Qt::Key_Delete: - if (_selectVoxelMode->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode)) { deleteVoxelUnderCursor(); } break; @@ -846,7 +730,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_6: case Qt::Key_7: case Qt::Key_8: - _swatch.handleEvent(event->key(), _eyedropperMode->isChecked()); + _swatch.handleEvent(event->key(), Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode)); break; default: event->ignore(); @@ -925,7 +809,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { if (event->buttons().testFlag(Qt::LeftButton)) { maybeEditVoxelUnderCursor(); - } else if (event->buttons().testFlag(Qt::RightButton) && checkedVoxelModeAction() != 0) { + } else if (event->buttons().testFlag(Qt::RightButton) && !Menu::getInstance()->isVoxelModeActionChecked()) { deleteVoxelUnderCursor(); } } @@ -973,7 +857,7 @@ void Application::mousePressEvent(QMouseEvent* event) { _isHoverVoxelSounding = true; } - } else if (event->button() == Qt::RightButton && checkedVoxelModeAction() != 0) { + } else if (event->button() == Qt::RightButton && !Menu::getInstance()->isVoxelModeActionChecked()) { deleteVoxelUnderCursor(); } } @@ -1032,7 +916,7 @@ const bool USE_MOUSEWHEEL = false; void Application::wheelEvent(QWheelEvent* event) { // Wheel Events disabled for now because they are also activated by touch look pitch up/down. if (USE_MOUSEWHEEL && (activeWindow() == _window)) { - if (checkedVoxelModeAction() == 0) { + if (Menu::getInstance()->isVoxelModeActionChecked()) { event->ignore(); return; } @@ -1092,7 +976,7 @@ void Application::sendAvatarFaceVideoMessage(int frameCount, const QByteArray& d void Application::timer() { gettimeofday(&_timerEnd, NULL); - if (_testPing->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { sendPingPackets(); } @@ -1168,8 +1052,8 @@ void Application::terminate() { LeapManager::terminate(); - if (_settingsAutosave->isChecked()) { - saveSettings(); + if (Menu::getInstance()->isOptionChecked(MenuOption::SettingsAutosave)) { + Menu::getInstance()->saveSettings(); _settings->sync(); } @@ -1184,24 +1068,6 @@ void Application::terminate() { } } -void Application::sendAvatarVoxelURLMessage(const QUrl& url) { - uint16_t ownerID = NodeList::getInstance()->getOwnerID(); - - if (ownerID == UNKNOWN_NODE_ID) { - return; // we don't yet know who we are - } - QByteArray message; - - char packetHeader[MAX_PACKET_HEADER_BYTES]; - int numBytesPacketHeader = populateTypeAndVersion((unsigned char*) packetHeader, PACKET_TYPE_AVATAR_VOXEL_URL); - - message.append(packetHeader, numBytesPacketHeader); - message.append((const char*)&ownerID, sizeof(ownerID)); - message.append(url.toEncoded()); - - controlledBroadcastToNodes((unsigned char*)message.data(), message.size(), & NODE_TYPE_AVATAR_MIXER, 1); -} - static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& dataBytes) { // record the packet for stats-tracking Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AVATARS).updateValue(dataBytes); @@ -1251,34 +1117,16 @@ void Application::processAvatarFaceVideoMessage(unsigned char* packetData, size_ void Application::checkBandwidthMeterClick() { // ... to be called upon button release - if (_bandwidthDisplayOn->isChecked() && + if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth) && glm::compMax(glm::abs(glm::ivec2(_mouseX - _mouseDragStartedX, _mouseY - _mouseDragStartedY))) <= BANDWIDTH_METER_CLICK_MAX_DRAG_LENGTH && _bandwidthMeter.isWithinArea(_mouseX, _mouseY, _glWidget->width(), _glWidget->height())) { // The bandwidth meter is visible, the click didn't get dragged too far and // we actually hit the bandwidth meter - bandwidthDetails(); + Menu::getInstance()->bandwidthDetails(); } } -void Application::bandwidthDetails() { - - if (! _bandwidthDialog) { - _bandwidthDialog = new BandwidthDialog(_glWidget, getBandwidthMeter()); - connect(_bandwidthDialog, SIGNAL(closed()), SLOT(bandwidthDetailsClosed())); - - _bandwidthDialog->show(); - } - _bandwidthDialog->raise(); -} - -void Application::bandwidthDetailsClosed() { - - QDialog* dlg = _bandwidthDialog; - _bandwidthDialog = NULL; - delete dlg; -} - void Application::voxelStatsDetails() { if (!_voxelStatsDialog) { _voxelStatsDialog = new VoxelStatsDialog(_glWidget, &_voxelSceneStats); @@ -1294,131 +1142,12 @@ void Application::voxelStatsDetailsClosed() { delete dlg; } -void Application::editPreferences() { - QDialog dialog(_glWidget); - dialog.setWindowTitle("Interface Preferences"); - QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom); - dialog.setLayout(layout); - - QFormLayout* form = new QFormLayout(); - layout->addLayout(form, 1); - - const int QLINE_MINIMUM_WIDTH = 400; - - QLineEdit* domainServerHostname = new QLineEdit(QString(NodeList::getInstance()->getDomainHostname())); - domainServerHostname->setMinimumWidth(QLINE_MINIMUM_WIDTH); - form->addRow("Domain server:", domainServerHostname); - - QLineEdit* avatarURL = new QLineEdit(_myAvatar.getVoxels()->getVoxelURL().toString()); - avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH); - form->addRow("Avatar URL:", avatarURL); - - QSpinBox* fieldOfView = new QSpinBox(); - fieldOfView->setMaximum(180); - fieldOfView->setMinimum(1); - fieldOfView->setValue(_fieldOfView); - form->addRow("Vertical Field of View (Degrees):", fieldOfView); - - QDoubleSpinBox* gyroCameraSensitivity = new QDoubleSpinBox(); - gyroCameraSensitivity->setValue(_gyroCameraSensitivity); - form->addRow("Gyro Camera Sensitivity (0 - 1):", gyroCameraSensitivity); - - QDoubleSpinBox* leanScale = new QDoubleSpinBox(); - leanScale->setValue(_myAvatar.getLeanScale()); - form->addRow("Lean Scale:", leanScale); - - QSpinBox* audioJitterBufferSamples = new QSpinBox(); - audioJitterBufferSamples->setMaximum(10000); - audioJitterBufferSamples->setMinimum(-10000); - audioJitterBufferSamples->setValue(_audioJitterBufferSamples); - form->addRow("Audio Jitter Buffer Samples (0 for automatic):", audioJitterBufferSamples); - - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); - dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); - layout->addWidget(buttons); - - if (dialog.exec() != QDialog::Accepted) { - return; - } - - QByteArray newHostname; - - if (domainServerHostname->text().size() > 0) { - // the user input a new hostname, use that - newHostname = domainServerHostname->text().toLocal8Bit(); - } else { - // the user left the field blank, use the default hostname - newHostname = QByteArray(DEFAULT_DOMAIN_HOSTNAME); - } - - // check if the domain server hostname is new - if (memcmp(NodeList::getInstance()->getDomainHostname(), newHostname.constData(), newHostname.size()) != 0) { - - NodeList::getInstance()->clear(); - - // kill the local voxels - _voxels.killLocalVoxels(); - - // reset the environment to default - _environment.resetToDefault(); - - // set the new hostname - NodeList::getInstance()->setDomainHostname(newHostname.constData()); - } - - QUrl url(avatarURL->text()); - _myAvatar.getVoxels()->setVoxelURL(url); - sendAvatarVoxelURLMessage(url); - - _gyroCameraSensitivity = gyroCameraSensitivity->value(); - _myAvatar.setLeanScale(leanScale->value()); - _audioJitterBufferSamples = audioJitterBufferSamples->value(); - if (!shouldDynamicallySetJitterBuffer()) { - _audio.setJitterBufferSamples(_audioJitterBufferSamples); - } - _fieldOfView = fieldOfView->value(); - resizeGL(_glWidget->width(), _glWidget->height()); - -} - -void Application::pair() { - PairingHandler::sendPairRequest(); -} - -void Application::setRenderMirrored(bool mirrored) { - if (mirrored) { - _manualFirstPerson->setChecked(false); - _manualThirdPerson->setChecked(false); - } -} - -void Application::setNoise(bool noise) { - _myAvatar.setNoise(noise); -} - void Application::setFullscreen(bool fullscreen) { _window->setWindowState(fullscreen ? (_window->windowState() | Qt::WindowFullScreen) : (_window->windowState() & ~Qt::WindowFullScreen)); updateCursor(); } -void Application::setRenderFirstPerson(bool firstPerson) { - if (firstPerson) { - _lookingInMirror->setChecked(false); - _manualThirdPerson->setChecked(false); - } else { - _manualThirdPerson->trigger(); - } -} - -void Application::setRenderThirdPerson(bool thirdPerson) { - if (thirdPerson) { - _lookingInMirror->setChecked(false); - _manualFirstPerson->setChecked(false); - } -} - void Application::increaseAvatarSize() { if ((1.f + SCALING_RATIO) * _myAvatar.getNewScale() < MAX_SCALE) { _myAvatar.setNewScale((1.f + SCALING_RATIO) * _myAvatar.getNewScale()); @@ -1443,11 +1172,6 @@ void Application::setFrustumOffset(bool frustumOffset) { resizeGL(_glWidget->width(), _glWidget->height()); } -void Application::cycleFrustumRenderMode() { - _frustumDrawingMode = (FrustumDrawMode)((_frustumDrawingMode + 1) % FRUSTUM_DRAW_MODE_COUNT); - updateFrustumRenderModeAction(); -} - void Application::setRenderWarnings(bool renderWarnings) { _voxels.setRenderPipelineWarnings(renderWarnings); } @@ -1522,15 +1246,6 @@ void Application::disableOcclusionCulling(bool disableOcclusionCulling) { _myAvatar.setWantOcclusionCulling(!disableOcclusionCulling); } -void Application::updateVoxelModeActions() { - // only the sender can be checked - foreach (QAction* action, _voxelModeActions->actions()) { - if (action->isChecked() && action != sender()) { - action->setChecked(false); - } - } -} - void Application::sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail) { unsigned char* bufferOut; int sizeOut; @@ -1559,23 +1274,6 @@ void Application::resetSwatchColors() { _swatch.reset(); } -static QIcon createSwatchIcon(const QColor& color) { - QPixmap map(16, 16); - map.fill(color); - return QIcon(map); -} - -void Application::chooseVoxelPaintColor() { - QColor selected = QColorDialog::getColor(_voxelPaintColor->data().value(), _glWidget, "Voxel Paint Color"); - if (selected.isValid()) { - _voxelPaintColor->setData(selected); - _voxelPaintColor->setIcon(createSwatchIcon(selected)); - } - - // restore the main window's active state - _window->activateWindow(); -} - const int MAXIMUM_EDIT_VOXEL_MESSAGE_SIZE = 1500; struct SendVoxelsOperationArgs { unsigned char* newBaseOctCode; @@ -1942,193 +1640,7 @@ void Application::pasteVoxels() { } void Application::initMenu() { - QMenuBar* menuBar = new QMenuBar(); - _window->setMenuBar(menuBar); - QMenu* fileMenu = menuBar->addMenu("File"); - QAction* quitAction = fileMenu->addAction("Quit", this, SLOT(quit()), Qt::CTRL | Qt::Key_Q); - quitAction->setMenuRole(QAction::QuitRole); - - QMenu* editMenu = menuBar->addMenu("Edit"); - QAction* preferencesAction = editMenu->addAction("Preferences...", this, SLOT(editPreferences()), Qt::CTRL | Qt::Key_Comma); - preferencesAction->setMenuRole(QAction::PreferencesRole); - - QMenu* pairMenu = menuBar->addMenu("Pair"); - pairMenu->addAction("Pair", this, SLOT(pair())); - - QMenu* optionsMenu = menuBar->addMenu("Options"); - (_lookingInMirror = optionsMenu->addAction("Mirror", this, SLOT(setRenderMirrored(bool)), Qt::Key_H))->setCheckable(true); - - - (_noise = optionsMenu->addAction("Noise", this, SLOT(setNoise(bool)), Qt::Key_N))->setCheckable(true); - (_gyroLook = optionsMenu->addAction("Smooth Gyro Look"))->setCheckable(true); - _gyroLook->setChecked(true); - (_showHeadMouse = optionsMenu->addAction("Head Mouse"))->setCheckable(true); - _showHeadMouse->setChecked(false); - (_transmitterDrives = optionsMenu->addAction("Transmitter Drive"))->setCheckable(true); - _transmitterDrives->setChecked(true); - (_gravityUse = optionsMenu->addAction("Use Gravity"))->setCheckable(true); - _gravityUse->setChecked(true); - _gravityUse->setShortcut(Qt::SHIFT | Qt::Key_G); - (_testPing = optionsMenu->addAction("Test Ping"))->setCheckable(true); - _testPing->setChecked(true); - (_fullScreenMode = optionsMenu->addAction("Fullscreen", this, SLOT(setFullscreen(bool)), Qt::Key_F))->setCheckable(true); - optionsMenu->addAction("Webcam", &_webcam, SLOT(setEnabled(bool)))->setCheckable(true); - optionsMenu->addAction("Skeleton Tracking", &_webcam, SLOT(setSkeletonTrackingOn(bool)))->setCheckable(true); - (_wantCollisionsOn = optionsMenu->addAction("Turn collisions On", this, SLOT(toggleWantCollisionsOn())))->setCheckable(true); - _wantCollisionsOn->setChecked(true); - optionsMenu->addAction("Cycle Webcam Send Mode", _webcam.getGrabber(), SLOT(cycleVideoSendMode())); - optionsMenu->addAction("Webcam Texture", _webcam.getGrabber(), SLOT(setDepthOnly(bool)))->setCheckable(true); - optionsMenu->addAction("Go Home", this, SLOT(goHome()), Qt::CTRL | Qt::Key_G); - - QMenu* audioMenu = menuBar->addMenu("Audio"); - (_echoAudioMode = audioMenu->addAction("Echo Audio"))->setCheckable(true); - _rawAudioMicrophoneMix = audioMenu->addAction("Mix RAW Song", this, SLOT(toggleMixedSong())); - - QMenu* renderMenu = menuBar->addMenu("Render"); - (_renderVoxels = renderMenu->addAction("Voxels", this, SLOT(setRenderVoxels(bool)), Qt::SHIFT | Qt::Key_V))->setCheckable(true); - _renderVoxels->setChecked(true); - (_renderVoxelTextures = renderMenu->addAction("Voxel Textures"))->setCheckable(true); - (_renderStarsOn = renderMenu->addAction("Stars"))->setCheckable(true); - _renderStarsOn->setChecked(true); - _renderStarsOn->setShortcut(Qt::Key_Asterisk); - (_renderAtmosphereOn = renderMenu->addAction("Atmosphere"))->setCheckable(true); - _renderAtmosphereOn->setChecked(true); - _renderAtmosphereOn->setShortcut(Qt::SHIFT | Qt::Key_A); - (_renderGroundPlaneOn = renderMenu->addAction("Ground Plane"))->setCheckable(true); - _renderGroundPlaneOn->setChecked(true); - (_renderAvatarsOn = renderMenu->addAction("Avatars"))->setCheckable(true); - _renderAvatarsOn->setChecked(true); - (_renderAvatarBalls = renderMenu->addAction("Avatar as Balls"))->setCheckable(true); - _renderAvatarBalls->setChecked(false); - renderMenu->addAction("Cycle Voxel Mode", _myAvatar.getVoxels(), SLOT(cycleMode())); - renderMenu->addAction("Cycle Face Mode", &_myAvatar.getHead().getFace(), SLOT(cycleRenderMode())); - (_renderFrameTimerOn = renderMenu->addAction("Show Timer"))->setCheckable(true); - _renderFrameTimerOn->setChecked(false); - (_renderLookatOn = renderMenu->addAction("Lookat Vectors"))->setCheckable(true); - _renderLookatOn->setChecked(false); - (_renderLookatIndicatorOn = renderMenu->addAction("Lookat Indicator"))->setCheckable(true); - _renderLookatIndicatorOn->setChecked(true); - (_renderParticleSystemOn = renderMenu->addAction("Particle System"))->setCheckable(true); - (_manualFirstPerson = renderMenu->addAction( - "First Person", this, SLOT(setRenderFirstPerson(bool)), Qt::Key_P))->setCheckable(true); - (_manualThirdPerson = renderMenu->addAction( - "Third Person", this, SLOT(setRenderThirdPerson(bool))))->setCheckable(true); - renderMenu->addAction("Increase Avatar Size", this, SLOT(increaseAvatarSize()), Qt::Key_Plus); - renderMenu->addAction("Decrease Avatar Size", this, SLOT(decreaseAvatarSize()), Qt::Key_Minus); - renderMenu->addAction("Reset Avatar Size", this, SLOT(resetAvatarSize())); - - - QMenu* toolsMenu = menuBar->addMenu("Tools"); - (_renderStatsOn = toolsMenu->addAction("Stats"))->setCheckable(true); - _renderStatsOn->setShortcut(Qt::Key_Slash); - (_logOn = toolsMenu->addAction("Log"))->setCheckable(true); - _logOn->setChecked(false); - _logOn->setShortcut(Qt::CTRL | Qt::Key_L); - (_oscilloscopeOn = toolsMenu->addAction("Audio Oscilloscope"))->setCheckable(true); - _oscilloscopeOn->setChecked(true); - (_bandwidthDisplayOn = toolsMenu->addAction("Bandwidth Display"))->setCheckable(true); - _bandwidthDisplayOn->setChecked(true); - toolsMenu->addAction("Bandwidth Details", this, SLOT(bandwidthDetails())); - toolsMenu->addAction("Voxel Stats Details", this, SLOT(voxelStatsDetails())); - - - QMenu* voxelMenu = menuBar->addMenu("Voxels"); - _voxelModeActions = new QActionGroup(this); - _voxelModeActions->setExclusive(false); // exclusivity implies one is always checked - - (_addVoxelMode = voxelMenu->addAction( - "Add Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_V))->setCheckable(true); - _voxelModeActions->addAction(_addVoxelMode); - (_deleteVoxelMode = voxelMenu->addAction( - "Delete Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_R))->setCheckable(true); - _voxelModeActions->addAction(_deleteVoxelMode); - (_colorVoxelMode = voxelMenu->addAction( - "Color Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_B))->setCheckable(true); - _voxelModeActions->addAction(_colorVoxelMode); - (_selectVoxelMode = voxelMenu->addAction( - "Select Voxel Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_O))->setCheckable(true); - _voxelModeActions->addAction(_selectVoxelMode); - (_eyedropperMode = voxelMenu->addAction( - "Get Color Mode", this, SLOT(updateVoxelModeActions()), Qt::Key_G))->setCheckable(true); - _voxelModeActions->addAction(_eyedropperMode); - - voxelMenu->addAction("Decrease Voxel Size", this, SLOT(decreaseVoxelSize()), QKeySequence::ZoomOut); - voxelMenu->addAction("Increase Voxel Size", this, SLOT(increaseVoxelSize()), QKeySequence::ZoomIn); - voxelMenu->addAction("Reset Swatch Colors", this, SLOT(resetSwatchColors())); - - _voxelPaintColor = voxelMenu->addAction("Voxel Paint Color", this, - SLOT(chooseVoxelPaintColor()), Qt::META | Qt::Key_C); - _swatch.setAction(_voxelPaintColor); - - QColor paintColor(128, 128, 128); - _voxelPaintColor->setData(paintColor); - _voxelPaintColor->setIcon(createSwatchIcon(paintColor)); - (_destructiveAddVoxel = voxelMenu->addAction("Create Voxel is Destructive"))->setCheckable(true); - - voxelMenu->addAction("Export Voxels", this, SLOT(exportVoxels()), Qt::CTRL | Qt::Key_E); - voxelMenu->addAction("Import Voxels", this, SLOT(importVoxels()), Qt::CTRL | Qt::Key_I); - voxelMenu->addAction("Import Voxels to Clipboard", this, SLOT(importVoxelsToClipboard()), Qt::SHIFT | Qt::CTRL | Qt::Key_I); - voxelMenu->addAction("Cut Voxels", this, SLOT(cutVoxels()), Qt::CTRL | Qt::Key_X); - voxelMenu->addAction("Copy Voxels", this, SLOT(copyVoxels()), Qt::CTRL | Qt::Key_C); - voxelMenu->addAction("Paste Voxels", this, SLOT(pasteVoxels()), Qt::CTRL | Qt::Key_V); - - QMenu* debugMenu = menuBar->addMenu("Debug"); - - QMenu* frustumMenu = debugMenu->addMenu("View Frustum Debugging Tools"); - (_frustumOn = frustumMenu->addAction("Display Frustum"))->setCheckable(true); - _frustumOn->setShortcut(Qt::SHIFT | Qt::Key_F); - _frustumRenderModeAction = frustumMenu->addAction( - "Render Mode", this, SLOT(cycleFrustumRenderMode()), Qt::SHIFT | Qt::Key_R); - updateFrustumRenderModeAction(); - - debugMenu->addAction("Run Timing Tests", this, SLOT(runTests())); - debugMenu->addAction("Calculate Tree Stats", this, SLOT(doTreeStats()), Qt::SHIFT | Qt::Key_S); - - QMenu* renderDebugMenu = debugMenu->addMenu("Render Debugging Tools"); - (_renderPipelineWarnings = renderDebugMenu->addAction("Show Render Pipeline Warnings", - this, SLOT(setRenderWarnings(bool))))->setCheckable(true); - renderDebugMenu->addAction("Kill Local Voxels", this, SLOT(doKillLocalVoxels()), Qt::CTRL | Qt::Key_K); - renderDebugMenu->addAction("Randomize Voxel TRUE Colors", this, SLOT(doRandomizeVoxelColors()), Qt::CTRL | Qt::Key_R); - renderDebugMenu->addAction("FALSE Color Voxels Randomly", this, SLOT(doFalseRandomizeVoxelColors())); - renderDebugMenu->addAction("FALSE Color Voxel Every Other Randomly", this, SLOT(doFalseRandomizeEveryOtherVoxelColors())); - renderDebugMenu->addAction("FALSE Color Voxels by Distance", this, SLOT(doFalseColorizeByDistance())); - renderDebugMenu->addAction("FALSE Color Voxel Out of View", this, SLOT(doFalseColorizeInView())); - renderDebugMenu->addAction("FALSE Color Occluded Voxels", this, SLOT(doFalseColorizeOccluded()), Qt::CTRL | Qt::Key_O); - renderDebugMenu->addAction("FALSE Color Occluded V2 Voxels", this, SLOT(doFalseColorizeOccludedV2()), Qt::CTRL | Qt::Key_P); - renderDebugMenu->addAction("FALSE Color By Source", this, SLOT(doFalseColorizeBySource()), Qt::CTRL | Qt::SHIFT | Qt::Key_S); - renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()), Qt::CTRL | Qt::Key_T); - - (_shouldLowPassFilter = debugMenu->addAction("Test: LowPass filter"))->setCheckable(true); - - debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true); - debugMenu->addAction("Disable Lower Resolution While Moving", this, SLOT(disableLowResMoving(bool)))->setCheckable(true); - debugMenu->addAction("Disable Delta Sending", this, SLOT(disableDeltaSending(bool)))->setCheckable(true); - (_occlusionCulling = debugMenu->addAction("Disable Occlusion Culling", this, SLOT(disableOcclusionCulling(bool)), - Qt::SHIFT | Qt::Key_C))->setCheckable(true); - - (_renderCoverageMap = debugMenu->addAction("Render Coverage Map"))->setCheckable(true); - _renderCoverageMap->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_O); - (_renderCoverageMapV2 = debugMenu->addAction("Render Coverage Map V2"))->setCheckable(true); - _renderCoverageMapV2->setShortcut(Qt::SHIFT | Qt::CTRL | Qt::Key_P); - - (_simulateLeapHand = debugMenu->addAction("Simulate Leap Hand"))->setCheckable(true); - (_testRaveGlove = debugMenu->addAction("Test RaveGlove"))->setCheckable(true); - - QMenu* audioDebugMenu = debugMenu->addMenu("Audio Debugging Tools"); - audioDebugMenu->addAction("Listen Mode Normal", this, SLOT(setListenModeNormal()), Qt::CTRL | Qt::Key_1); - audioDebugMenu->addAction("Listen Mode Point/Radius", this, SLOT(setListenModePoint()), Qt::CTRL | Qt::Key_2); - audioDebugMenu->addAction("Listen Mode Single Source", this, SLOT(setListenModeSingleSource()), Qt::CTRL | Qt::Key_3); - - QMenu* settingsMenu = menuBar->addMenu("Settings"); - (_settingsAutosave = settingsMenu->addAction("Autosave"))->setCheckable(true); - _settingsAutosave->setChecked(true); - settingsMenu->addAction("Load settings", this, SLOT(loadSettings())); - settingsMenu->addAction("Save settings", this, SLOT(saveSettings())); - settingsMenu->addAction("Import settings", this, SLOT(importSettings())); - settingsMenu->addAction("Export settings", this, SLOT(exportSettings())); - - _networkAccessManager = new QNetworkAccessManager(this); } void Application::setListenModeNormal() { @@ -2154,59 +1666,6 @@ void Application::setListenModeSingleSource() { } } -void Application::toggleMixedSong() { - if (_audio.getSongFileBytes() == 0) { - QString filename = QFileDialog::getOpenFileName(_glWidget, - tr("Choose RAW Audio file"), - QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), - tr("RAW Audio file (*.raw)")); - - QByteArray filenameArray = filename.toLocal8Bit(); - _audio.importSongToMixWithMicrophone(filenameArray.data()); - resetSongMixMenuItem(); - } else { - _audio.stopMixingSongWithMicrophone(); - resetSongMixMenuItem(); - } -} - -void Application::toggleWantCollisionsOn() { - _myAvatar.setWantCollisionsOn(_wantCollisionsOn->isChecked()); -} - -void Application::resetSongMixMenuItem() { - if (_audio.getSongFileBytes() == 0) { - _rawAudioMicrophoneMix->setText("Mix RAW Song"); - } else { - _rawAudioMicrophoneMix->setText("Stop Mixing Song"); - } - -} - -void Application::updateFrustumRenderModeAction() { - switch (_frustumDrawingMode) { - default: - case FRUSTUM_DRAW_MODE_ALL: - _frustumRenderModeAction->setText("Render Mode - All"); - break; - case FRUSTUM_DRAW_MODE_VECTORS: - _frustumRenderModeAction->setText("Render Mode - Vectors"); - break; - case FRUSTUM_DRAW_MODE_PLANES: - _frustumRenderModeAction->setText("Render Mode - Planes"); - break; - case FRUSTUM_DRAW_MODE_NEAR_PLANE: - _frustumRenderModeAction->setText("Render Mode - Near"); - break; - case FRUSTUM_DRAW_MODE_FAR_PLANE: - _frustumRenderModeAction->setText("Render Mode - Far"); - break; - case FRUSTUM_DRAW_MODE_KEYHOLE: - _frustumRenderModeAction->setText("Render Mode - Keyhole"); - break; - } -} - void Application::runTests() { runTimingTests(); } @@ -2240,7 +1699,9 @@ void Application::init() { OculusManager::connect(); if (OculusManager::isConnected()) { - QMetaObject::invokeMethod(_fullScreenMode, "trigger", Qt::QueuedConnection); + QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), + "trigger", + Qt::QueuedConnection); } LeapManager::initialize(); @@ -2248,22 +1709,22 @@ void Application::init() { gettimeofday(&_timerStart, NULL); gettimeofday(&_lastTimeUpdated, NULL); - loadSettings(); - if (!shouldDynamicallySetJitterBuffer()) { - _audio.setJitterBufferSamples(_audioJitterBufferSamples); + Menu::getInstance()->loadSettings(); + if (Menu::getInstance()->getAudioJitterBufferSamples() != 0) { + _audio.setJitterBufferSamples(Menu::getInstance()->getAudioJitterBufferSamples()); } qDebug("Loaded settings.\n"); - sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL()); + Avatar::sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL()); _palette.init(_glWidget->width(), _glWidget->height()); - _palette.addAction(_addVoxelMode, 0, 0); - _palette.addAction(_deleteVoxelMode, 0, 1); + _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); + _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelDeleteMode), 0, 1); _palette.addTool(&_swatch); - _palette.addAction(_colorVoxelMode, 0, 2); - _palette.addAction(_eyedropperMode, 0, 3); - _palette.addAction(_selectVoxelMode, 0, 4); + _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelColorMode), 0, 2); + _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelGetColorMode), 0, 3); + _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelSelectMode), 0, 4); _pieMenu.init("./resources/images/hifi-interface-tools-v2-pie.svg", _glWidget->width(), @@ -2475,7 +1936,7 @@ void Application::update(float deltaTime) { } _mouseVoxel.s = 0.0f; - if (checkedVoxelModeAction() != 0 && + if (!Menu::getInstance()->isVoxelModeActionChecked() && (fabs(_myAvatar.getVelocity().x) + fabs(_myAvatar.getVelocity().y) + fabs(_myAvatar.getVelocity().z)) / 3 < MAX_AVATAR_EDIT_VELOCITY) { @@ -2500,7 +1961,7 @@ void Application::update(float deltaTime) { _mouseVoxel.z = _mouseVoxelScale * floorf(pt.z / _mouseVoxelScale); _mouseVoxel.s = _mouseVoxelScale; } - if (_addVoxelMode->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)) { // use the face to determine the side on which to create a neighbor _mouseVoxel.x += faceVector.x * _mouseVoxel.s; _mouseVoxel.y += faceVector.y * _mouseVoxel.s; @@ -2510,7 +1971,8 @@ void Application::update(float deltaTime) { } else { _mouseVoxel.s = 0.0f; } - } else if (_addVoxelMode->isChecked() || _selectVoxelMode->isChecked()) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) + || Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) { // place the voxel a fixed distance away float worldMouseVoxelScale = _mouseVoxelScale * TREE_SCALE; glm::vec3 pt = mouseRayOrigin + mouseRayDirection * (2.0f + worldMouseVoxelScale * 0.5f); @@ -2520,19 +1982,14 @@ void Application::update(float deltaTime) { _mouseVoxel.s = _mouseVoxelScale; } - if (_deleteVoxelMode->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode)) { // red indicates deletion _mouseVoxel.red = 255; _mouseVoxel.green = _mouseVoxel.blue = 0; - } else if (_selectVoxelMode->isChecked()) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelSelectMode)) { // yellow indicates deletion _mouseVoxel.red = _mouseVoxel.green = 255; _mouseVoxel.blue = 0; - } else { // _addVoxelMode->isChecked() || _colorVoxelMode->isChecked() - QColor paintColor = _voxelPaintColor->data().value(); - _mouseVoxel.red = paintColor.red(); - _mouseVoxel.green = paintColor.green(); - _mouseVoxel.blue = paintColor.blue(); } // if we just edited, use the currently selected voxel as the "last" for drag detection @@ -2559,8 +2016,8 @@ void Application::update(float deltaTime) { } // Leap finger-sensing device - LeapManager::enableFakeFingers(_simulateLeapHand->isChecked()); - _myAvatar.getHand().setRaveGloveActive(_testRaveGlove->isChecked()); + LeapManager::enableFakeFingers(Menu::getInstance()->isOptionChecked(MenuOption::SimulateLeapHand)); + _myAvatar.getHand().setRaveGloveActive(Menu::getInstance()->isOptionChecked(MenuOption::TestRaveGlove)); LeapManager::nextFrame(_myAvatar); // Read serial port interface devices @@ -2599,57 +2056,42 @@ void Application::update(float deltaTime) { } // Simulate myself - if (_gravityUse->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) { _myAvatar.setGravity(_environment.getGravity(_myAvatar.getPosition())); } else { _myAvatar.setGravity(glm::vec3(0.0f, 0.0f, 0.0f)); } - if (_transmitterDrives->isChecked() && _myTransmitter.isConnected()) { - _myAvatar.simulate(deltaTime, &_myTransmitter, _gyroCameraSensitivity); + if (Menu::getInstance()->isOptionChecked(MenuOption::TransmitterDrive) && _myTransmitter.isConnected()) { + _myAvatar.simulate(deltaTime, &_myTransmitter, Menu::getInstance()->getGyroCameraSensitivity()); } else { - _myAvatar.simulate(deltaTime, NULL, _gyroCameraSensitivity); + _myAvatar.simulate(deltaTime, NULL, Menu::getInstance()->getGyroCameraSensitivity()); } if (!OculusManager::isConnected()) { - if (_lookingInMirror->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { _myCamera.setMode(CAMERA_MODE_MIRROR); _myCamera.setModeShiftRate(100.0f); } - } else if (_manualFirstPerson->isChecked()) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) { if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) { _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setModeShiftRate(1.0f); } - } else if (_manualThirdPerson->isChecked()) { + } else { if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) { _myCamera.setMode(CAMERA_MODE_THIRD_PERSON); _myCamera.setModeShiftRate(1.0f); } - } else { - const float THIRD_PERSON_SHIFT_VELOCITY = 1000.0f; - const float TIME_BEFORE_SHIFT_INTO_FIRST_PERSON = 0.75f; - const float TIME_BEFORE_SHIFT_INTO_THIRD_PERSON = 0.1f; - - if ((_myAvatar.getElapsedTimeStopped() > TIME_BEFORE_SHIFT_INTO_FIRST_PERSON) - && (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON)) { - _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); - _myCamera.setModeShiftRate(1.0f); - } - if ((_myAvatar.getSpeed() > THIRD_PERSON_SHIFT_VELOCITY) - && (_myAvatar.getElapsedTimeMoving() > TIME_BEFORE_SHIFT_INTO_THIRD_PERSON) - && (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON)) { - _myCamera.setMode(CAMERA_MODE_THIRD_PERSON); - _myCamera.setModeShiftRate(1000.0f); - } } } // Update bandwidth dialog, if any - if (_bandwidthDialog) { - _bandwidthDialog->update(); + BandwidthDialog* bandwidthDialog = Menu::getInstance()->getBandwidthDialog(); + if (bandwidthDialog) { + bandwidthDialog->update(); } if (_voxelStatsDialog) { _voxelStatsDialog->update(); @@ -2662,7 +2104,7 @@ void Application::update(float deltaTime) { _audio.eventuallyAnalyzePing(); #endif - if (_renderParticleSystemOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleSystem)) { updateParticleSystem(deltaTime); } } @@ -2675,7 +2117,7 @@ void Application::updateAvatar(float deltaTime) { _yawFromTouch = 0.f; // Update my avatar's state from gyros and/or webcam - _myAvatar.updateFromGyrosAndOrWebcam(_gyroLook->isChecked(), + _myAvatar.updateFromGyrosAndOrWebcam(Menu::getInstance()->isOptionChecked(MenuOption::GyroLook), _pitchFromTouch); if (_serialHeadSensor.isActive()) { @@ -2761,7 +2203,7 @@ void Application::updateAvatar(float deltaTime) { // once in a while, send my voxel url const float AVATAR_VOXEL_URL_SEND_INTERVAL = 1.0f; // seconds if (shouldDo(AVATAR_VOXEL_URL_SEND_INTERVAL, deltaTime)) { - sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL()); + Avatar::sendAvatarVoxelURLMessage(_myAvatar.getVoxels()->getVoxelURL()); } } @@ -2779,7 +2221,7 @@ void Application::updateAvatar(float deltaTime) { _paintingVoxel.y >= 0.0 && _paintingVoxel.y <= 1.0 && _paintingVoxel.z >= 0.0 && _paintingVoxel.z <= 1.0) { - PACKET_TYPE message = (_destructiveAddVoxel->isChecked() ? + PACKET_TYPE message = (Menu::getInstance()->isOptionChecked(MenuOption::DestructiveAddVoxel) ? PACKET_TYPE_SET_VOXEL_DESTRUCTIVE : PACKET_TYPE_SET_VOXEL); sendVoxelEditMessage(message, _paintingVoxel); } @@ -2991,7 +2433,7 @@ void Application::displaySide(Camera& whichCamera) { // transform by eye offset // flip x if in mirror mode (also requires reversing winding order for backface culling) - if (_lookingInMirror->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Mirror)) { glScalef(-1.0f, 1.0f, 1.0f); glFrontFace(GL_CW); @@ -3018,7 +2460,7 @@ void Application::displaySide(Camera& whichCamera) { // Setup 3D lights (after the camera transform, so that they are positioned in world space) setupWorldLight(whichCamera); - if (_renderStarsOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { if (!_stars.getFileLoaded()) { _stars.readInput(STAR_FILE, STAR_CACHE_FILE, 0); } @@ -3026,7 +2468,7 @@ void Application::displaySide(Camera& whichCamera) { // compute starfield alpha based on distance from atmosphere float alpha = 1.0f; - if (_renderAtmosphereOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { const EnvironmentData& closestData = _environment.getClosestData(whichCamera.getPosition()); float height = glm::distance(whichCamera.getPosition(), closestData.getAtmosphereCenter()); if (height < closestData.getAtmosphereInnerRadius()) { @@ -3043,7 +2485,7 @@ void Application::displaySide(Camera& whichCamera) { } // draw the sky dome - if (_renderAtmosphereOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Atmosphere)) { _environment.renderAtmospheres(whichCamera); } @@ -3065,7 +2507,7 @@ void Application::displaySide(Camera& whichCamera) { glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR); //draw a grid ground plane.... - if (_renderGroundPlaneOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::GroundPlane)) { // draw grass plane with fog glEnable(GL_FOG); glEnable(GL_NORMALIZE); @@ -3088,8 +2530,8 @@ void Application::displaySide(Camera& whichCamera) { //renderGroundPlaneGrid(EDGE_SIZE_GROUND_PLANE, _audio.getCollisionSoundMagnitude()); } // Draw voxels - if (_renderVoxels->isChecked()) { - _voxels.render(_renderVoxelTextures->isChecked()); + if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { + _voxels.render(Menu::getInstance()->isOptionChecked(MenuOption::VoxelTextures)); } // restore default, white specular @@ -3101,7 +2543,7 @@ void Application::displaySide(Camera& whichCamera) { glPushMatrix(); glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE); renderMouseVoxelGrid(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s); - if (_addVoxelMode->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode)) { // use a contrasting color so that we can see what we're doing glColor3ub(_mouseVoxel.red + 128, _mouseVoxel.green + 128, _mouseVoxel.blue + 128); } else { @@ -3119,7 +2561,7 @@ void Application::displaySide(Camera& whichCamera) { _myAvatar.renderScreenTint(SCREEN_TINT_BEFORE_AVATARS, whichCamera); - if (_renderAvatarsOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { // Render avatars of other nodes NodeList* nodeList = NodeList::getInstance(); @@ -3134,8 +2576,8 @@ void Application::displaySide(Camera& whichCamera) { if (isLookingAtMyAvatar(avatar)) { avatar->getHead().setLookAtPosition(_myCamera.getPosition()); } - avatar->render(false, _renderAvatarBalls->isChecked()); - avatar->setDisplayingLookatVectors(_renderLookatOn->isChecked()); + avatar->render(false, Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)); + avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); } node->unlock(); @@ -3145,27 +2587,32 @@ void Application::displaySide(Camera& whichCamera) { if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { _myAvatar.getHead().setLookAtPosition(_myCamera.getPosition()); } - _myAvatar.render(_lookingInMirror->isChecked(), _renderAvatarBalls->isChecked()); - _myAvatar.setDisplayingLookatVectors(_renderLookatOn->isChecked()); + _myAvatar.render(Menu::getInstance()->isOptionChecked(MenuOption::Mirror), + Menu::getInstance()->isOptionChecked(MenuOption::AvatarAsBalls)); + _myAvatar.setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); - if (_renderLookatIndicatorOn->isChecked() && _isLookingAtOtherAvatar) { + if (Menu::getInstance()->isOptionChecked(MenuOption::LookAtIndicator) && _isLookingAtOtherAvatar) { renderLookatIndicator(_lookatOtherPosition, whichCamera); } } _myAvatar.renderScreenTint(SCREEN_TINT_AFTER_AVATARS, whichCamera); - if (_renderParticleSystemOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleSystem)) { if (_particleSystemInitialized) { _particleSystem.render(); } } // Render the world box - if (!_lookingInMirror->isChecked() && _renderStatsOn->isChecked()) { renderWorldBox(); } + if (!Menu::getInstance()->isOptionChecked(MenuOption::Mirror) && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { + renderWorldBox(); + } // brad's frustum for debugging - if (_frustumOn->isChecked()) renderViewFrustum(_viewFrustum); + if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayFrustum)) { + renderViewFrustum(_viewFrustum); + } // render voxel fades if they exist if (_voxelFades.size() > 0) { @@ -3200,14 +2647,16 @@ void Application::displayOverlay() { #ifndef _WIN32 _audio.render(_glWidget->width(), _glWidget->height()); - if (_oscilloscopeOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Oscilloscope)) { _audioScope.render(20, _glWidget->height() - 200); } #endif //noiseTest(_glWidget->width(), _glWidget->height()); - if (_showHeadMouse->isChecked() && !_lookingInMirror->isChecked() && USING_INVENSENSE_MPU9150) { + if (Menu::getInstance()->isOptionChecked(MenuOption::HeadMouse) + && !Menu::getInstance()->isOptionChecked(MenuOption::Mirror) + && USING_INVENSENSE_MPU9150) { // Display small target box at center or head mouse target that can also be used to measure LOD glColor3f(1.0, 1.0, 1.0); glDisable(GL_LINE_SMOOTH); @@ -3233,14 +2682,26 @@ void Application::displayOverlay() { glLineWidth(1.0f); glPointSize(1.0f); - if (_renderStatsOn->isChecked()) { displayStats(); } + if (Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { + displayStats(); + } // testing rendering coverage map - if (_renderCoverageMapV2->isChecked()) { renderCoverageMapV2(); } - if (_renderCoverageMap->isChecked()) { renderCoverageMap(); } - if (_bandwidthDisplayOn->isChecked()) { _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); } + if (Menu::getInstance()->isOptionChecked(MenuOption::CoverageMapV2)) { + renderCoverageMapV2(); + } + + if (Menu::getInstance()->isOptionChecked(MenuOption::CoverageMap)) { + renderCoverageMap(); + } + + if (Menu::getInstance()->isOptionChecked(MenuOption::Bandwidth)) { + _bandwidthMeter.render(_glWidget->width(), _glWidget->height()); + } - if (_logOn->isChecked()) { LogDisplay::instance.render(_glWidget->width(), _glWidget->height()); } + if (Menu::getInstance()->isOptionChecked(MenuOption::Log)) { + LogDisplay::instance.render(_glWidget->width(), _glWidget->height()); + } // Show chat entry field if (_chatEntryOn) { @@ -3248,7 +2709,7 @@ void Application::displayOverlay() { } // Show on-screen msec timer - if (_renderFrameTimerOn->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::FrameTimer)) { char frameTimer[10]; uint64_t mSecsNow = floor(usecTimestampNow() / 1000.0 + 0.5); sprintf(frameTimer, "%d\n", (int)(mSecsNow % 1000)); @@ -3256,7 +2717,6 @@ void Application::displayOverlay() { drawtext(_glWidget->width() - 102, _glWidget->height() - 22, 0.30, 0, 1.0, 0, frameTimer, 1, 1, 1); } - // Stats at upper right of screen about who domain server is telling us about glPointSize(1.0f); char nodes[100]; @@ -3285,8 +2745,11 @@ void Application::displayOverlay() { _palette.render(_glWidget->width(), _glWidget->height()); - if (_eyedropperMode->isChecked() && _voxelPaintColor->data().value() != _swatch.getColor()) { - QColor color = _voxelPaintColor->data().value(); + QAction* paintColorAction = NULL; + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode) + && (paintColorAction = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor))->data().value() + != _swatch.getColor()) { + QColor color = paintColorAction->data().value(); TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50); const char line1[] = "Assign this color to a swatch"; const char line2[] = "by choosing a key from 1 to 8."; @@ -3343,7 +2806,7 @@ void Application::displayStats() { _fps, _packetsPerSecond, (float)_bytesPerSecond * 8.f / 1000000.f); drawtext(10, statsVerticalOffset + 15, 0.10f, 0, 1.0, 0, stats); - if (_testPing->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { int pingAudio = 0, pingAvatar = 0, pingVoxel = 0, pingVoxelMax = 0; NodeList* nodeList = NodeList::getInstance(); @@ -3637,7 +3100,8 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { glLineWidth(1.0); glBegin(GL_LINES); - if (_frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || _frustumDrawingMode == FRUSTUM_DRAW_MODE_VECTORS) { + if (Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_ALL + || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_VECTORS) { // Calculate the origin direction vectors glm::vec3 lookingAt = position + (direction * 0.2f); glm::vec3 lookingAtUp = position + (up * 0.2f); @@ -3659,8 +3123,9 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { glVertex3f(lookingAtRight.x, lookingAtRight.y, lookingAtRight.z); } - if (_frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || _frustumDrawingMode == FRUSTUM_DRAW_MODE_PLANES - || _frustumDrawingMode == FRUSTUM_DRAW_MODE_NEAR_PLANE) { + if (Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_ALL + || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_PLANES + || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_NEAR_PLANE) { // Drawing the bounds of the frustum // viewFrustum.getNear plane - bottom edge glColor3f(1,0,0); @@ -3680,8 +3145,9 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { glVertex3f(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z); } - if (_frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || _frustumDrawingMode == FRUSTUM_DRAW_MODE_PLANES - || _frustumDrawingMode == FRUSTUM_DRAW_MODE_FAR_PLANE) { + if (Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_ALL + || 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!!! glVertex3f(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z); @@ -3700,7 +3166,8 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { glVertex3f(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z); } - if (_frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || _frustumDrawingMode == FRUSTUM_DRAW_MODE_PLANES) { + if (Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_ALL + || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_PLANES) { // RIGHT PLANE IS CYAN // right plane - bottom edge - viewFrustum.getNear to distant glColor3f(0,1,1); @@ -3724,7 +3191,8 @@ void Application::renderViewFrustum(ViewFrustum& viewFrustum) { glEnd(); glEnable(GL_LIGHTING); - if (_frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || _frustumDrawingMode == FRUSTUM_DRAW_MODE_KEYHOLE) { + if (Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_ALL + || Menu::getInstance()->getFrustumDrawMode() == FRUSTUM_DRAW_MODE_KEYHOLE) { // Draw the keyhole float keyholeRadius = viewFrustum.getKeyholeRadius(); if (keyholeRadius > 0.0f) { @@ -3814,15 +3282,18 @@ void Application::injectVoxelAddedSoundEffect() { } bool Application::maybeEditVoxelUnderCursor() { - if (_addVoxelMode->isChecked() || _colorVoxelMode->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelAddMode) + || Menu::getInstance()->isOptionChecked(MenuOption::VoxelColorMode)) { if (_mouseVoxel.s != 0) { - PACKET_TYPE message = (_destructiveAddVoxel->isChecked() ? - PACKET_TYPE_SET_VOXEL_DESTRUCTIVE : PACKET_TYPE_SET_VOXEL); + PACKET_TYPE message = Menu::getInstance()->isOptionChecked(MenuOption::DestructiveAddVoxel) + ? PACKET_TYPE_SET_VOXEL_DESTRUCTIVE + : PACKET_TYPE_SET_VOXEL; sendVoxelEditMessage(message, _mouseVoxel); // create the voxel locally so it appears immediately _voxels.createVoxel(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s, - _mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue, _destructiveAddVoxel->isChecked()); + _mouseVoxel.red, _mouseVoxel.green, _mouseVoxel.blue, + Menu::getInstance()->isOptionChecked(MenuOption::DestructiveAddVoxel)); // Implement voxel fade effect VoxelFade fade(VoxelFade::FADE_OUT, 1.0f, 1.0f, 1.0f); @@ -3841,7 +3312,7 @@ bool Application::maybeEditVoxelUnderCursor() { _justEditedVoxel = true; } - } else if (_deleteVoxelMode->isChecked()) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelDeleteMode)) { deleteVoxelUnderCursor(); VoxelFade fade(VoxelFade::FADE_OUT, 1.0f, 1.0f, 1.0f); const float VOXEL_BOUNDS_ADJUST = 0.01f; @@ -3852,7 +3323,7 @@ bool Application::maybeEditVoxelUnderCursor() { fade.voxelDetails.s = _mouseVoxel.s + slightlyBigger + slightlyBigger; _voxelFades.push_back(fade); - } else if (_eyedropperMode->isChecked()) { + } else if (Menu::getInstance()->isOptionChecked(MenuOption::VoxelGetColorMode)) { eyedropperVoxelUnderCursor(); } else { return false; @@ -3893,8 +3364,9 @@ void Application::eyedropperVoxelUnderCursor() { selectedNode->getColor()[BLUE_INDEX]); if (selectedColor.isValid()) { - _voxelPaintColor->setData(selectedColor); - _voxelPaintColor->setIcon(createSwatchIcon(selectedColor)); + QAction* voxelPaintColorAction = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor); + voxelPaintColorAction->setData(selectedColor); + voxelPaintColorAction->setIcon(Swatch::createIcon(selectedColor)); } } } @@ -3959,17 +3431,6 @@ void Application::updateCursor() { Qt::BlankCursor : Qt::ArrowCursor); } -// when QActionGroup is set to non-exclusive, it doesn't return anything as checked; -// hence, we must check ourselves -QAction* Application::checkedVoxelModeAction() const { - foreach (QAction* action, _voxelModeActions->actions()) { - if (action->isChecked()) { - return action; - } - } - return 0; -} - void Application::attachNewHeadToNode(Node* newNode) { if (newNode->getLinkedData() == NULL) { newNode->setLinkedData(new Avatar(newNode)); @@ -4067,7 +3528,7 @@ void Application::queueVoxelPacket(sockaddr& senderAddress, unsigned char* packe } void Application::processVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) { - PerformanceWarning warn(_renderPipelineWarnings->isChecked(),"processVoxelPacket()"); + PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "processVoxelPacket()"); ssize_t messageLength = packetLength; // note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA or PACKET_TYPE_VOXEL_DATA_MONOCHROME @@ -4087,7 +3548,7 @@ void Application::processVoxelPacket(sockaddr& senderAddress, unsigned char* pac } } // fall through to piggyback message - if (_renderVoxels->isChecked()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { Node* voxelServer = NodeList::getInstance()->nodeWithAddress(&senderAddress); if (voxelServer && socketMatch(voxelServer->getActiveSocket(), &senderAddress)) { voxelServer->lock(); @@ -4170,117 +3631,6 @@ void* Application::networkReceive(void* args) { return NULL; } -void Application::scanMenuBar(settingsAction modifySetting, QSettings* set) { - if (!_window->menuBar()) { - return; - } - - QList menus = _window->menuBar()->findChildren(); - - for (QList::const_iterator it = menus.begin(); menus.end() != it; ++it) { - scanMenu(*it, modifySetting, set); - } -} - -void Application::scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set) { - QList actions = menu->actions(); - - set->beginGroup(menu->title()); - for (QList::const_iterator it = actions.begin(); actions.end() != it; ++it) { - if ((*it)->menu()) { - scanMenu((*it)->menu(), modifySetting, set); - } - if ((*it)->isCheckable()) { - modifySetting(set, *it); - } - } - set->endGroup(); -} - -void Application::loadAction(QSettings* set, QAction* action) { - if (action->isChecked() != set->value(action->text(), action->isChecked()).toBool()) { - action->trigger(); - } -} - -void Application::saveAction(QSettings* set, QAction* action) { - set->setValue(action->text(), action->isChecked()); -} - -void Application::loadSettings(QSettings* settings) { - if (!settings) { - settings = getSettings(); - } - - _gyroCameraSensitivity = loadSetting(settings, "gyroCameraSensitivity", 0.5f); - _audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0); - _fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES); - - settings->beginGroup("View Frustum Offset Camera"); - // in case settings is corrupt or missing loadSetting() will check for NaN - _viewFrustumOffsetYaw = loadSetting(settings, "viewFrustumOffsetYaw" , 0.0f); - _viewFrustumOffsetPitch = loadSetting(settings, "viewFrustumOffsetPitch" , 0.0f); - _viewFrustumOffsetRoll = loadSetting(settings, "viewFrustumOffsetRoll" , 0.0f); - _viewFrustumOffsetDistance = loadSetting(settings, "viewFrustumOffsetDistance", 0.0f); - _viewFrustumOffsetUp = loadSetting(settings, "viewFrustumOffsetUp" , 0.0f); - settings->endGroup(); - - scanMenuBar(&Application::loadAction, settings); - getAvatar()->loadData(settings); - _swatch.loadData(settings); -} - - -void Application::saveSettings(QSettings* settings) { - if (!settings) { - settings = getSettings(); - } - - settings->setValue("gyroCameraSensitivity", _gyroCameraSensitivity); - settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples); - settings->setValue("fieldOfView", _fieldOfView); - settings->beginGroup("View Frustum Offset Camera"); - settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffsetYaw); - settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffsetPitch); - settings->setValue("viewFrustumOffsetRoll", _viewFrustumOffsetRoll); - settings->setValue("viewFrustumOffsetDistance", _viewFrustumOffsetDistance); - settings->setValue("viewFrustumOffsetUp", _viewFrustumOffsetUp); - settings->endGroup(); - - scanMenuBar(&Application::saveAction, settings); - getAvatar()->saveData(settings); - _swatch.saveData(settings); - - // ask the NodeList to save its data - NodeList::getInstance()->saveData(settings); -} - -void Application::importSettings() { - QString locationDir(QStandardPaths::displayName(QStandardPaths::DesktopLocation)); - QString fileName = QFileDialog::getOpenFileName(_window, - tr("Open .ini config file"), - locationDir, - tr("Text files (*.ini)")); - if (fileName != "") { - QSettings tmp(fileName, QSettings::IniFormat); - loadSettings(&tmp); - } -} - -void Application::exportSettings() { - QString locationDir(QStandardPaths::displayName(QStandardPaths::DesktopLocation)); - QString fileName = QFileDialog::getSaveFileName(_window, - tr("Save .ini config file"), - locationDir, - tr("Text files (*.ini)")); - if (fileName != "") { - QSettings tmp(fileName, QSettings::IniFormat); - saveSettings(&tmp); - tmp.sync(); - } -} - - void Application::updateParticleSystem(float deltaTime) { if (!_particleSystemInitialized) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 29f7e8bea7..0deaa23f2e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -30,6 +30,7 @@ #include "BandwidthMeter.h" #include "Camera.h" #include "Environment.h" +#include "GLCanvas.h" #include "PacketHeaders.h" #include "ParticleSystem.h" #include "SerialInterface.h" @@ -104,21 +105,22 @@ public: Camera* getCamera() { return &_myCamera; } ViewFrustum* getViewFrustum() { return &_viewFrustum; } VoxelSystem* getVoxels() { return &_voxels; } - QSettings* getSettings() { return _settings; } Environment* getEnvironment() { return &_environment; } SerialInterface* getSerialHeadSensor() { return &_serialHeadSensor; } Webcam* getWebcam() { return &_webcam; } + QGLWidget* getGLWidget() { return _glWidget; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } - bool shouldEchoAudio() { return _echoAudioMode->isChecked(); } - bool shouldLowPassFilter() { return _shouldLowPassFilter->isChecked(); } - - bool shouldDynamicallySetJitterBuffer() { return _audioJitterBufferSamples == 0; } + QSettings* getSettings() { return _settings; } + Swatch* getSwatch() { return &_swatch; } + QMainWindow* getWindow() { return _window; } QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; } GeometryCache* getGeometryCache() { return &_geometryCache; } TextureCache* getTextureCache() { return &_textureCache; } - void resetSongMixMenuItem(); + static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, + const char* nodeTypes, int numNodeTypes); + void setupWorldLight(Camera& whichCamera); virtual void nodeAdded(Node* node); @@ -133,21 +135,11 @@ private slots: void idle(); void terminate(); - void bandwidthDetails(); - void editPreferences(); - void bandwidthDetailsClosed(); - void voxelStatsDetails(); void voxelStatsDetailsClosed(); - void pair(); - - void setRenderMirrored(bool mirrored); - void setNoise(bool noise); void setFullscreen(bool fullscreen); - void setRenderFirstPerson(bool firstPerson); - void setRenderThirdPerson(bool thirdPerson); void increaseAvatarSize(); void decreaseAvatarSize(); void resetAvatarSize(); @@ -156,7 +148,6 @@ private slots: void renderLineToTouchedVoxel(); void setFrustumOffset(bool frustumOffset); - void cycleFrustumRenderMode(); void setRenderWarnings(bool renderWarnings); void setRenderVoxels(bool renderVoxels); @@ -175,15 +166,9 @@ private slots: void disableLowResMoving(bool disableLowResMoving); void disableDeltaSending(bool disableDeltaSending); void disableOcclusionCulling(bool disableOcclusionCulling); - void updateVoxelModeActions(); void decreaseVoxelSize(); void increaseVoxelSize(); void resetSwatchColors(); - void chooseVoxelPaintColor(); - void loadSettings(QSettings* set = NULL); - void saveSettings(QSettings* set = NULL); - void importSettings(); - void exportSettings(); void exportVoxels(); void importVoxels(); void importVoxelsToClipboard(); @@ -194,9 +179,6 @@ private slots: void setListenModeNormal(); void setListenModePoint(); void setListenModeSingleSource(); - void toggleMixedSong(); - void toggleWantCollisionsOn(); - void renderCoverageMap(); void renderCoverageMapsRecursively(CoverageMap* map); @@ -212,13 +194,9 @@ private slots: private: void resetCamerasOnResizeGL(Camera& camera, int width, int height); - static void controlledBroadcastToNodes(unsigned char* broadcastData, size_t dataBytes, - const char* nodeTypes, int numNodeTypes); - static void sendVoxelServerAddScene(); static bool sendVoxelsOperation(VoxelNode* node, void* extraData); static void sendVoxelEditMessage(PACKET_TYPE type, VoxelDetail& detail); - static void sendAvatarVoxelURLMessage(const QUrl& url); static void processAvatarVoxelURLMessage(unsigned char* packetData, size_t dataBytes); static void processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes); static void sendPingPackets(); @@ -258,8 +236,6 @@ private: void updateCursor(); - QAction* checkedVoxelModeAction() const; - static void attachNewHeadToNode(Node *newNode); static void* networkReceive(void* args); // network receive thread @@ -267,69 +243,13 @@ private: void processVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); void queueVoxelPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength); - // methodes handling menu settings - typedef void(*settingsAction)(QSettings*, QAction*); - static void loadAction(QSettings* set, QAction* action); - static void saveAction(QSettings* set, QAction* action); - void scanMenuBar(settingsAction modifySetting, QSettings* set); - void scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set); QMainWindow* _window; QGLWidget* _glWidget; - QAction* _lookingInMirror; // Are we currently rendering one's own head as if in mirror? - QAction* _echoAudioMode; // Are we asking the mixer to echo back our audio? - QAction* _shouldLowPassFilter; // Use test lowpass filter - QAction* _gyroLook; // Whether to allow the gyro data from head to move your view - QAction* _renderAvatarBalls; // Switch between voxels and joints/balls for avatar render - QAction* _showHeadMouse; // Whether the have the mouse near edge of screen move your view - QAction* _transmitterDrives; // Whether to have Transmitter data move/steer the Avatar - QAction* _gravityUse; // Whether gravity is on or not - QAction* _testPing; // Whether to display ping or not - QAction* _renderVoxels; // Whether to render voxels - QAction* _renderVoxelTextures; // Whether to render noise textures on voxels - QAction* _renderStarsOn; // Whether to display the stars - QAction* _renderAtmosphereOn; // Whether to display the atmosphere - QAction* _renderGroundPlaneOn; // Whether to display the ground plane - QAction* _renderAvatarsOn; // Whether to render avatars - QAction* _renderStatsOn; // Whether to show onscreen text overlay with stats - QAction* _renderFrameTimerOn; // Whether to show onscreen text overlay with stats - QAction* _renderLookatOn; // Whether to show lookat vectors from avatar eyes if looking at something - QAction* _renderLookatIndicatorOn; - QAction* _renderParticleSystemOn; - QAction* _manualFirstPerson; // Whether to force first-person mode - QAction* _manualThirdPerson; // Whether to force third-person mode - QAction* _logOn; // Whether to show on-screen log - QAction* _oscilloscopeOn; // Whether to show the oscilloscope - QAction* _bandwidthDisplayOn; // Whether to show on-screen bandwidth bars - QActionGroup* _voxelModeActions; // The group of voxel edit mode actions - QAction* _addVoxelMode; // Whether add voxel mode is enabled - QAction* _deleteVoxelMode; // Whether delete voxel mode is enabled - QAction* _colorVoxelMode; // Whether color voxel mode is enabled - QAction* _selectVoxelMode; // Whether select voxel mode is enabled - QAction* _eyedropperMode; // Whether voxel color eyedropper mode is enabled - QAction* _voxelPaintColor; // The color with which to paint voxels - QAction* _destructiveAddVoxel; // when doing voxel editing do we want them to be destructive - QAction* _frustumOn; // Whether or not to display the debug view frustum - QAction* _fullScreenMode; // whether we are in full screen mode - QAction* _frustumRenderModeAction; - QAction* _settingsAutosave; // Whether settings are saved automatically - QAction* _rawAudioMicrophoneMix; // Mixing of a RAW audio file with microphone stream for rave gloves - QAction* _noise; - QAction* _occlusionCulling; - QAction* _wantCollisionsOn; - QAction* _renderPipelineWarnings; - - QAction* _renderCoverageMapV2; - QAction* _renderCoverageMap; - - QAction* _simulateLeapHand; // When there's no Leap, use this to pretend there is one and feed fake hand data - QAction* _testRaveGlove; // Test fancy sparkle-rave-glove mode - QAction* _followMode; BandwidthMeter _bandwidthMeter; - BandwidthDialog* _bandwidthDialog; VoxelStatsDialog* _voxelStatsDialog; SerialInterface _serialHeadSensor; @@ -358,16 +278,6 @@ private: bool _wantToKillLocalVoxels; ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. - - enum FrustumDrawMode { FRUSTUM_DRAW_MODE_ALL, FRUSTUM_DRAW_MODE_VECTORS, FRUSTUM_DRAW_MODE_PLANES, - FRUSTUM_DRAW_MODE_NEAR_PLANE, FRUSTUM_DRAW_MODE_FAR_PLANE, FRUSTUM_DRAW_MODE_KEYHOLE, FRUSTUM_DRAW_MODE_COUNT }; - FrustumDrawMode _frustumDrawingMode; - - float _viewFrustumOffsetYaw; // the following variables control yaw, pitch, roll and distance form regular - float _viewFrustumOffsetPitch; // camera to the offset camera - float _viewFrustumOffsetRoll; - float _viewFrustumOffsetDistance; - float _viewFrustumOffsetUp; Oscilloscope _audioScope; @@ -383,11 +293,6 @@ private: Environment _environment; int _headMouseX, _headMouseY; - float _gyroCameraSensitivity; - - int _audioJitterBufferSamples; // Number of extra samples to wait before starting audio playback - - float _fieldOfView; // In Degrees, doesn't apply to HMD like Oculus HandControl _handControl; diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index add56df80c..449ec55a16 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -23,6 +23,7 @@ #include "Application.h" #include "Audio.h" +#include "Menu.h" #include "Util.h" // Uncomment the following definition to test audio device latency by copying output to input @@ -112,7 +113,7 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o // + 12 for 3 floats for position + float for bearing + 1 attenuation byte unsigned char dataPacket[MAX_PACKET_SIZE]; - PACKET_TYPE packetType = (Application::getInstance()->shouldEchoAudio()) + PACKET_TYPE packetType = Menu::getInstance()->isOptionChecked(MenuOption::EchoAudio) ? PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO : PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO; @@ -153,38 +154,6 @@ inline void Audio::performIO(int16_t* inputLeft, int16_t* outputLeft, int16_t* o memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation)); currentPacketPtr += sizeof(headOrientation); - // check if we have a song to add to our audio - if (_songFileBytes > 0 && _songFileStream->tellg() != -1) { - // iterate over BUFFER_LENGTH_SAMPLES_PER_CHANNEL from the song file and add that to our audio - for (int i = 0; i < BUFFER_LENGTH_SAMPLES_PER_CHANNEL; i++) { - int16_t songSample = 0; - - _songFileStream->read((char*) &songSample, sizeof(songSample)); - - // attenuate the song samples since they will be loud - const float SONG_SAMPLE_ATTENUATION = 0.25; - songSample *= SONG_SAMPLE_ATTENUATION; - - // add the song sample to the output and input buffersg - inputLeft[i] = inputLeft[i] + songSample; - outputLeft[i] = outputLeft[i] + songSample; - outputRight[i] = outputLeft[i] + songSample; - } - } else if (_songFileStream) { - // close the stream - _songFileStream->close(); - - // delete the _songFileStream - delete _songFileStream; - _songFileStream = NULL; - - // reset the _songFileBytes back to zero - _songFileBytes = 0; - - // call Application stopMixingSong method to fix menu item - Application::getInstance()->resetSongMixMenuItem(); - } - // copy the audio data to the last BUFFER_LENGTH_BYTES bytes of the data packet memcpy(currentPacketPtr, inputLeft, BUFFER_LENGTH_BYTES_PER_CHANNEL); @@ -415,8 +384,6 @@ Audio::Audio(Oscilloscope* scope, int16_t initialJitterBufferSamples) : _collisionSoundDuration(0.0f), _proceduralEffectSample(0), _heartbeatMagnitude(0.0f), - _songFileStream(NULL), - _songFileBytes(0), _listenMode(AudioRingBuffer::NORMAL), _listenRadius(0.0f) { @@ -489,24 +456,6 @@ Audio::~Audio() { delete[] _echoSamplesLeft; } - -void Audio::importSongToMixWithMicrophone(const char* filename) { - _songFileStream = new std::ifstream(filename); - - long begin = _songFileStream->tellg(); - _songFileStream->seekg(0, std::ios::end); - long end = _songFileStream->tellg(); - - // go back to the beginning - _songFileStream->seekg(0); - - _songFileBytes = end - begin; -} - -void Audio::stopMixingSongWithMicrophone() { - _songFileBytes = 0; -} - void Audio::addReceivedAudioToBuffer(unsigned char* receivedData, int receivedBytes) { const int NUM_INITIAL_PACKETS_DISCARD = 3; const int STANDARD_DEVIATION_SAMPLE_COUNT = 500; @@ -528,7 +477,7 @@ void Audio::addReceivedAudioToBuffer(unsigned char* receivedData, int receivedBy // Set jitter buffer to be a multiple of the measured standard deviation const int MAX_JITTER_BUFFER_SAMPLES = RING_BUFFER_LENGTH_SAMPLES / 2; const float NUM_STANDARD_DEVIATIONS = 3.f; - if (Application::getInstance()->shouldDynamicallySetJitterBuffer()) { + if (Menu::getInstance()->getAudioJitterBufferSamples() == 0) { float newJitterBufferSamples = (NUM_STANDARD_DEVIATIONS * _measuredJitter) / 1000.f * SAMPLE_RATE; @@ -646,7 +595,7 @@ void Audio::render(int screenWidth, int screenHeight) { sprintf(out, "%.0f\n", getJitterBufferSamples() / SAMPLE_RATE * 1000.f); drawtext(startX + jitterBufferPels - 5, topY - 9, 0.10, 0, 1, 0, out, 1, 0, 0); sprintf(out, "j %.1f\n", _measuredJitter); - if (Application::getInstance()->shouldDynamicallySetJitterBuffer()) { + if (Menu::getInstance()->getAudioJitterBufferSamples() == 0) { drawtext(startX + jitterBufferPels - 5, bottomY + 12, 0.10, 0, 1, 0, out, 1, 0, 0); } else { drawtext(startX, bottomY + 12, 0.10, 0, 1, 0, out, 1, 0, 0); diff --git a/interface/src/Audio.h b/interface/src/Audio.h index e60e33d613..8d6371a6f9 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -54,8 +54,6 @@ public: float getCollisionSoundMagnitude() { return _collisionSoundMagnitude; }; - int getSongFileBytes() { return _songFileBytes; } - void ping(); // Call periodically to eventually perform round trip time analysis, @@ -68,11 +66,6 @@ public: void addListenSource(int sourceID); void removeListenSource(int sourceID); void clearListenSources(); - - void importSongToMixWithMicrophone(const char* filename); - -public slots: - void stopMixingSongWithMicrophone(); private: PaStream* _stream; @@ -109,9 +102,7 @@ private: float _collisionSoundDuration; int _proceduralEffectSample; float _heartbeatMagnitude; - std::ifstream* _songFileStream; - int _songFileBytes; - + AudioRingBuffer::ListenMode _listenMode; float _listenRadius; std::vector _listenSources; diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp new file mode 100644 index 0000000000..2aaf782c4f --- /dev/null +++ b/interface/src/GLCanvas.cpp @@ -0,0 +1,67 @@ +// +// GLCanvas.cpp +// hifi +// +// Created by Stephen Birarda on 8/14/13. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#include "Application.h" + +#include "GLCanvas.h" + +void GLCanvas::initializeGL() { + Application::getInstance()->initializeGL(); + setAttribute(Qt::WA_AcceptTouchEvents); +} + +void GLCanvas::paintGL() { + Application::getInstance()->paintGL(); +} + +void GLCanvas::resizeGL(int width, int height) { + Application::getInstance()->resizeGL(width, height); +} + +void GLCanvas::keyPressEvent(QKeyEvent* event) { + Application::getInstance()->keyPressEvent(event); +} + +void GLCanvas::keyReleaseEvent(QKeyEvent* event) { + Application::getInstance()->keyReleaseEvent(event); +} + +void GLCanvas::mouseMoveEvent(QMouseEvent* event) { + Application::getInstance()->mouseMoveEvent(event); +} + +void GLCanvas::mousePressEvent(QMouseEvent* event) { + Application::getInstance()->mousePressEvent(event); +} + +void GLCanvas::mouseReleaseEvent(QMouseEvent* event) { + Application::getInstance()->mouseReleaseEvent(event); +} + +int updateTime = 0; +bool GLCanvas::event(QEvent* event) { + switch (event->type()) { + case QEvent::TouchBegin: + Application::getInstance()->touchBeginEvent(static_cast(event)); + event->accept(); + return true; + case QEvent::TouchEnd: + Application::getInstance()->touchEndEvent(static_cast(event)); + return true; + case QEvent::TouchUpdate: + Application::getInstance()->touchUpdateEvent(static_cast(event)); + return true; + default: + break; + } + return QGLWidget::event(event); +} + +void GLCanvas::wheelEvent(QWheelEvent* event) { + Application::getInstance()->wheelEvent(event); +} \ No newline at end of file diff --git a/interface/src/GLCanvas.h b/interface/src/GLCanvas.h new file mode 100644 index 0000000000..dd11916775 --- /dev/null +++ b/interface/src/GLCanvas.h @@ -0,0 +1,34 @@ +// +// GLCanvas.h +// hifi +// +// Created by Stephen Birarda on 8/14/13. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__GLCanvas__ +#define __hifi__GLCanvas__ + +#include + +/// customized canvas that simply forwards requests/events to the singleton application +class GLCanvas : public QGLWidget { +protected: + + virtual void initializeGL(); + virtual void paintGL(); + virtual void resizeGL(int width, int height); + + virtual void keyPressEvent(QKeyEvent* event); + virtual void keyReleaseEvent(QKeyEvent* event); + + virtual void mouseMoveEvent(QMouseEvent* event); + virtual void mousePressEvent(QMouseEvent* event); + virtual void mouseReleaseEvent(QMouseEvent* event); + + virtual bool event(QEvent* event); + + virtual void wheelEvent(QWheelEvent* event); +}; + +#endif /* defined(__hifi__GLCanvas__) */ diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index bb58799322..22d3241015 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -8,10 +8,672 @@ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Application.h" +#include "PairingHandler.h" #include "Menu.h" Menu* Menu::_instance = NULL; -void Menu::init() { +Menu* Menu::getInstance() { + if (!_instance) { + qDebug("First call to Menu::getInstance() - initing menu.\n"); + + _instance = new Menu(); + } + + return _instance; +} + +const ViewFrustumOffset DEFAULT_FRUSTUM_OFFSET = {-135.0f, 0.0f, 0.0f, 25.0f, 0.0f}; + +Menu::Menu() : + _bandwidthDialog(NULL), + _frustumDrawMode(FRUSTUM_DRAW_MODE_ALL), + _viewFrustumOffset(DEFAULT_FRUSTUM_OFFSET) +{ + QApplication *appInstance = Application::getInstance(); + QMenu* fileMenu = addMenu("File"); + (addActionToQMenuAndActionHash(fileMenu, + MenuOption::Quit, + appInstance, + SLOT(quit()), + Qt::CTRL | Qt::Key_Q))->setMenuRole(QAction::QuitRole); + + (addActionToQMenuAndActionHash(fileMenu, + MenuOption::Preferences, + this, + SLOT(editPreferences()), + Qt::CTRL | Qt::Key_Comma))->setMenuRole(QAction::PreferencesRole); + + QMenu* pairMenu = addMenu("Pair"); + addActionToQMenuAndActionHash(pairMenu, MenuOption::Pair, PairingHandler::getInstance(), SLOT(sendPairRequest())); + + + QMenu* optionsMenu = addMenu("Options"); + + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::Mirror, NULL, NULL, Qt::Key_H, false); + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::Noise, NULL, NULL, Qt::Key_N, false); + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::GyroLook, NULL, NULL, 0, true); + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::HeadMouse, NULL, NULL, 0); + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::TransmitterDrive, NULL, NULL, 0, true); + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::Gravity, NULL, NULL, Qt::SHIFT | Qt::Key_G, true); + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::TestPing, NULL, NULL, 0, true); + addCheckableActionToQMenuAndActionHash(optionsMenu, + MenuOption::Fullscreen, + appInstance, + SLOT(setFullscreen(bool)), + Qt::Key_F); + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::Webcam); + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::SkeletonTracking); + // optionsMenu->addAction("Webcam", &_webcam, SLOT(setEnabled(bool)))->setCheckable(true); + // optionsMenu->addAction("Skeleton Tracking", &_webcam, SLOT(setSkeletonTrackingOn(bool)))->setCheckable(true); + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::Collisions, NULL, NULL, 0, true); + addActionToQMenuAndActionHash(optionsMenu, MenuOption::WebcamMode); + addCheckableActionToQMenuAndActionHash(optionsMenu, MenuOption::WebcamTexture); + + // optionsMenu->addAction("Cycle Webcam Send Mode", _webcam.getGrabber(), SLOT(cycleVideoSendMode())); + // optionsMenu->addAction("Webcam Texture", _webcam.getGrabber(), SLOT(setDepthOnly(bool)))->setCheckable(true); + + addActionToQMenuAndActionHash(optionsMenu, MenuOption::GoHome, appInstance, SLOT(goHome()), Qt::CTRL | Qt::Key_G); + + QMenu* audioMenu = addMenu("Audio"); + addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::EchoAudio); + + QMenu* renderMenu = addMenu("Render"); + addCheckableActionToQMenuAndActionHash(renderMenu, + MenuOption::Voxels, + appInstance, + SLOT(setRenderVoxels(bool)), + Qt::SHIFT | Qt::Key_V, + true); + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::VoxelTextures); + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::Stars, NULL, NULL, 0, true); + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::Atmosphere, NULL, NULL, Qt::SHIFT | Qt::Key_A, true); + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::GroundPlane, NULL, NULL, 0, true); + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::Avatars, NULL, NULL, 0, true); + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::AvatarAsBalls); + + addActionToQMenuAndActionHash(renderMenu, MenuOption::VoxelMode); + + // renderMenu->addAction("Cycle Voxel Mode", _myAvatar.getVoxels(), SLOT(cycleMode())); + + addActionToQMenuAndActionHash(renderMenu, MenuOption::FaceMode); + + // renderMenu->addAction("Cycle Face Mode", &_myAvatar.getHead().getFace(), SLOT(cycleRenderMode())); + + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::FrameTimer); + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::LookAtVectors); + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::LookAtIndicator, NULL, NULL, 0, true); + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::ParticleSystem); + addCheckableActionToQMenuAndActionHash(renderMenu, MenuOption::FirstPerson, NULL, NULL, Qt::Key_P, true); + + addActionToQMenuAndActionHash(renderMenu, MenuOption::IncreaseAvatarSize); + addActionToQMenuAndActionHash(renderMenu, MenuOption::DecreaseAvatarSize); + addActionToQMenuAndActionHash(renderMenu, MenuOption::ResetAvatarSize); + + // renderMenu->addAction("Increase Avatar Size", this, SLOT(increaseAvatarSize()), Qt::Key_Plus); + // renderMenu->addAction("Decrease Avatar Size", this, SLOT(decreaseAvatarSize()), Qt::Key_Minus); + // renderMenu->addAction("Reset Avatar Size", this, SLOT(resetAvatarSize())); + + QMenu* toolsMenu = addMenu("Tools"); + addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Stats, NULL, NULL, Qt::Key_Slash); + addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Log, NULL, NULL, Qt::CTRL | Qt::Key_L); + addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Oscilloscope, NULL, NULL, 0, true); + addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::Bandwidth, NULL, NULL, 0, true); + addActionToQMenuAndActionHash(toolsMenu, MenuOption::BandwidthDetails); + + // toolsMenu->addAction("Bandwidth Details", this, SLOT(bandwidthDetails())); + + addActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelStats); + + // toolsMenu->addAction("Voxel Stats Details", this, SLOT(voxelStatsDetails())); + + QMenu* voxelMenu = addMenu("Voxels"); + _voxelModeActionsGroup = new QActionGroup(this); + + QAction* addVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::VoxelAddMode, NULL, NULL, Qt::Key_V); + _voxelModeActionsGroup->addAction(addVoxelMode); + + QAction* deleteVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, + MenuOption::VoxelDeleteMode, + NULL, + NULL, + Qt::Key_R); + _voxelModeActionsGroup->addAction(deleteVoxelMode); + + QAction* colorVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, + MenuOption::VoxelColorMode, + NULL, + NULL, + Qt::Key_B); + _voxelModeActionsGroup->addAction(colorVoxelMode); + + QAction* selectVoxelMode = addCheckableActionToQMenuAndActionHash(voxelMenu, + MenuOption::VoxelSelectMode, + NULL, + NULL, + Qt::Key_O); + _voxelModeActionsGroup->addAction(selectVoxelMode); + + QAction* getColorMode = addCheckableActionToQMenuAndActionHash(voxelMenu, + MenuOption::VoxelGetColorMode, + NULL, + NULL, + Qt::Key_G); + _voxelModeActionsGroup->addAction(getColorMode); + + QAction* voxelPaintColor = addActionToQMenuAndActionHash(voxelMenu, + MenuOption::VoxelPaintColor, + this, + SLOT(chooseVoxelPaintColor()), + Qt::META | Qt::Key_C); + + Application::getInstance()->getSwatch()->setAction(voxelPaintColor); + + QColor paintColor(128, 128, 128); + voxelPaintColor->setData(paintColor); + voxelPaintColor->setIcon(Swatch::createIcon(paintColor)); + + addActionToQMenuAndActionHash(voxelMenu, + MenuOption::DecreaseVoxelSize, + appInstance, + SLOT(decreaseVoxelSize()), + QKeySequence::ZoomOut); + addActionToQMenuAndActionHash(voxelMenu, + MenuOption::IncreaseVoxelSize, + appInstance, + SLOT(increaseVoxelSize()), + QKeySequence::ZoomIn); + addActionToQMenuAndActionHash(voxelMenu, MenuOption::ResetSwatchColors, appInstance, SLOT(resetSwatchColors())); + + addCheckableActionToQMenuAndActionHash(voxelMenu, MenuOption::DestructiveAddVoxel); + + addActionToQMenuAndActionHash(voxelMenu, MenuOption::ExportVoxels, NULL, NULL, Qt::CTRL | Qt::Key_E); + addActionToQMenuAndActionHash(voxelMenu, MenuOption::ImportVoxels, NULL, NULL, Qt::CTRL | Qt::Key_I); + addActionToQMenuAndActionHash(voxelMenu, MenuOption::ImportVoxelsClipboard, NULL, NULL, Qt::SHIFT | Qt::CTRL | Qt::Key_I); + addActionToQMenuAndActionHash(voxelMenu, MenuOption::CutVoxels, NULL, NULL, Qt::CTRL | Qt::Key_X); + addActionToQMenuAndActionHash(voxelMenu, MenuOption::CopyVoxels, NULL, NULL, Qt::CTRL | Qt::Key_C); + addActionToQMenuAndActionHash(voxelMenu, MenuOption::PasteVoxels, NULL, NULL, Qt::CTRL | Qt::Key_V); + + QMenu* debugMenu = addMenu("Debug"); + + QMenu* frustumMenu = debugMenu->addMenu("View Frustum Debugging Tools"); + addCheckableActionToQMenuAndActionHash(frustumMenu, MenuOption::DisplayFrustum, NULL, NULL, Qt::SHIFT | Qt::Key_F); + + addActionToQMenuAndActionHash(frustumMenu, + MenuOption::FrustumRenderMode, + this, + SLOT(cycleFrustumRenderMode()), + Qt::SHIFT | Qt::Key_R); + updateFrustumRenderModeAction(); + + addActionToQMenuAndActionHash(debugMenu, MenuOption::RunTimingTests, NULL, NULL); + + // debugMenu->addAction("Run Timing Tests", this, SLOT(runTests())); + + addActionToQMenuAndActionHash(debugMenu, MenuOption::TreeStats, NULL, NULL, Qt::SHIFT | Qt::Key_S); + + // debugMenu->addAction("Calculate Tree Stats", this, SLOT(doTreeStats()), Qt::SHIFT | Qt::Key_S); + + QMenu* renderDebugMenu = debugMenu->addMenu("Render Debugging Tools"); + addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings, NULL, NULL); + + // (_renderPipelineWarnings = renderDebugMenu->addAction("Show Render Pipeline Warnings", + // this, SLOT(setRenderWarnings(bool))))->setCheckable(true); + + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::KillLocalVoxels, NULL, NULL, Qt::CTRL | Qt::Key_K); + + // renderDebugMenu->addAction("Kill Local Voxels", this, SLOT(doKillLocalVoxels()), Qt::CTRL | Qt::Key_K); + + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::RandomizeVoxelColors, NULL, NULL, Qt::CTRL | Qt::Key_R); + + // renderDebugMenu->addAction("Randomize Voxel TRUE Colors", this, SLOT(doRandomizeVoxelColors()), Qt::CTRL | Qt::Key_R); + + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorRandomly, NULL, NULL); + + // renderDebugMenu->addAction("FALSE Color Voxels Randomly", this, SLOT(doFalseRandomizeVoxelColors())); + + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorEveryOtherVoxel, NULL, NULL); + + // renderDebugMenu->addAction("FALSE Color Voxel Every Other Randomly", this, SLOT(doFalseRandomizeEveryOtherVoxelColors())); + + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorByDistance, NULL, NULL); + + // renderDebugMenu->addAction("FALSE Color Voxels by Distance", this, SLOT(doFalseColorizeByDistance())); + + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorOutOfView, NULL, NULL); + + // renderDebugMenu->addAction("FALSE Color Voxel Out of View", this, SLOT(doFalseColorizeInView())); + + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorOccluded, NULL, NULL, Qt::CTRL | Qt::Key_O); + + // renderDebugMenu->addAction("FALSE Color Occluded Voxels", this, SLOT(doFalseColorizeOccluded()), Qt::CTRL | Qt::Key_O); + + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorOccludedV2, NULL, NULL, Qt::CTRL | Qt::Key_P); + + // renderDebugMenu->addAction("FALSE Color Occluded V2 Voxels", this, SLOT(doFalseColorizeOccludedV2()), Qt::CTRL | Qt::Key_P); + + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorBySource, NULL, NULL, Qt::CTRL | Qt::SHIFT | Qt::Key_S); + + // renderDebugMenu->addAction("FALSE Color By Source", this, SLOT(doFalseColorizeBySource()), Qt::CTRL | Qt::SHIFT | Qt::Key_S); + + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::ShowTrueColors, NULL, NULL, Qt::CTRL | Qt::Key_T); + + // renderDebugMenu->addAction("Show TRUE Colors", this, SLOT(doTrueVoxelColors()), Qt::CTRL | Qt::Key_T); + + addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::LowPassFilter); + + addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::Monochrome, NULL, NULL); + + // debugMenu->addAction("Wants Monochrome", this, SLOT(setWantsMonochrome(bool)))->setCheckable(true); + + addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::DisableLowRes, NULL, NULL); + + // debugMenu->addAction("Disable Lower Resolution While Moving", this, SLOT(disableLowResMoving(bool)))->setCheckable(true); + + addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::DisableDeltaSending, NULL, NULL); + + // debugMenu->addAction("Disable Delta Sending", this, SLOT(disableDeltaSending(bool)))->setCheckable(true); + + addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::DisableOcclusionCulling, NULL, NULL, Qt::SHIFT | Qt::Key_C); + // (_occlusionCulling = debugMenu->addAction("Disable Occlusion Culling", this, SLOT(disableOcclusionCulling(bool)), + // Qt::SHIFT | Qt::Key_C))->setCheckable(true); + + addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::CoverageMap, NULL, NULL, Qt::SHIFT | Qt::CTRL | Qt::Key_O); + addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::CoverageMapV2, NULL, NULL, Qt::SHIFT | Qt::CTRL | Qt::Key_P); + addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::SimulateLeapHand); + addCheckableActionToQMenuAndActionHash(debugMenu, MenuOption::TestRaveGlove); + + QMenu* audioDebugMenu = debugMenu->addMenu("Audio Debugging Tools"); + addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::ListenModeNormal, NULL, NULL, Qt::CTRL | Qt::Key_1); + addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::ListenModePoint, NULL, NULL, Qt::CTRL | Qt::Key_2); + addActionToQMenuAndActionHash(audioDebugMenu, MenuOption::ListenModeSingleSource, NULL, NULL, Qt::CTRL | Qt::Key_3); + + // audioDebugMenu->addAction("Listen Mode Single Source", this, SLOT(setListenModeSingleSource()), Qt::CTRL | Qt::Key_3); + + QMenu* settingsMenu = addMenu("Settings"); + addCheckableActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsAutosave, NULL, NULL, 0, true); + addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsLoad, NULL, NULL); + addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsSave, NULL, NULL); + addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsImport, NULL, NULL); + addActionToQMenuAndActionHash(settingsMenu, MenuOption::SettingsExport, NULL, NULL); + + // _networkAccessManager = new QNetworkAccessManager(this); +} + +void Menu::loadSettings(QSettings* settings) { + if (!settings) { + settings = Application::getInstance()->getSettings(); + } + + _gyroCameraSensitivity = loadSetting(settings, "gyroCameraSensitivity", 0.5f); + _audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0); + _fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES); + + settings->beginGroup("View Frustum Offset Camera"); + // in case settings is corrupt or missing loadSetting() will check for NaN + _viewFrustumOffset.yaw = loadSetting(settings, "viewFrustumOffsetYaw", 0.0f); + _viewFrustumOffset.pitch = loadSetting(settings, "viewFrustumOffsetPitch", 0.0f); + _viewFrustumOffset.roll = loadSetting(settings, "viewFrustumOffsetRoll", 0.0f); + _viewFrustumOffset.distance = loadSetting(settings, "viewFrustumOffsetDistance", 0.0f); + _viewFrustumOffset.up = loadSetting(settings, "viewFrustumOffsetUp", 0.0f); + settings->endGroup(); + + scanMenuBar(&loadAction, settings); + Application::getInstance()->getAvatar()->loadData(settings); + Application::getInstance()->getSwatch()->loadData(settings); +} + +void Menu::saveSettings(QSettings* settings) { + if (!settings) { + settings = Application::getInstance()->getSettings(); + } + + settings->setValue("gyroCameraSensitivity", _gyroCameraSensitivity); + settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples); + settings->setValue("fieldOfView", _fieldOfView); + settings->beginGroup("View Frustum Offset Camera"); + settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw); + settings->setValue("viewFrustumOffsetPitch", _viewFrustumOffset.pitch); + settings->setValue("viewFrustumOffsetRoll", _viewFrustumOffset.roll); + settings->setValue("viewFrustumOffsetDistance", _viewFrustumOffset.distance); + settings->setValue("viewFrustumOffsetUp", _viewFrustumOffset.up); + settings->endGroup(); + + scanMenuBar(&saveAction, settings); + Application::getInstance()->getAvatar()->saveData(settings); + Application::getInstance()->getSwatch()->saveData(settings); + + // ask the NodeList to save its data + NodeList::getInstance()->saveData(settings); +} + +void Menu::importSettings() { + QString locationDir(QStandardPaths::displayName(QStandardPaths::DesktopLocation)); + QString fileName = QFileDialog::getOpenFileName(Application::getInstance()->getWindow(), + tr("Open .ini config file"), + locationDir, + tr("Text files (*.ini)")); + if (fileName != "") { + QSettings tmp(fileName, QSettings::IniFormat); + loadSettings(&tmp); + } +} + +void Menu::exportSettings() { + QString locationDir(QStandardPaths::displayName(QStandardPaths::DesktopLocation)); + QString fileName = QFileDialog::getSaveFileName(Application::getInstance()->getWindow(), + tr("Save .ini config file"), + locationDir, + tr("Text files (*.ini)")); + if (fileName != "") { + QSettings tmp(fileName, QSettings::IniFormat); + saveSettings(&tmp); + tmp.sync(); + } +} + + +void Menu::loadAction(QSettings* set, QAction* action) { + if (action->isChecked() != set->value(action->text(), action->isChecked()).toBool()) { + action->trigger(); + } +} + +void Menu::saveAction(QSettings* set, QAction* action) { + set->setValue(action->text(), action->isChecked()); +} + +void Menu::scanMenuBar(settingsAction modifySetting, QSettings* set) { + QList menus = this->findChildren(); + + for (QList::const_iterator it = menus.begin(); menus.end() != it; ++it) { + scanMenu(*it, modifySetting, set); + } +} + +void Menu::scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set) { + QList actions = menu->actions(); + + set->beginGroup(menu->title()); + for (QList::const_iterator it = actions.begin(); actions.end() != it; ++it) { + if ((*it)->menu()) { + scanMenu((*it)->menu(), modifySetting, set); + } + if ((*it)->isCheckable()) { + modifySetting(set, *it); + } + } + set->endGroup(); +} + +void Menu::handleViewFrustumOffsetKeyModifier(int key) { + const float VIEW_FRUSTUM_OFFSET_DELTA = 0.5f; + const float VIEW_FRUSTUM_OFFSET_UP_DELTA = 0.05f; + + switch (key) { + case Qt::Key_BracketLeft: + _viewFrustumOffset.yaw -= VIEW_FRUSTUM_OFFSET_DELTA; + break; + + case Qt::Key_BracketRight: + _viewFrustumOffset.yaw += VIEW_FRUSTUM_OFFSET_DELTA; + break; + + case Qt::Key_BraceLeft: + _viewFrustumOffset.pitch -= VIEW_FRUSTUM_OFFSET_DELTA; + break; + + case Qt::Key_BraceRight: + _viewFrustumOffset.pitch += VIEW_FRUSTUM_OFFSET_DELTA; + break; + + case Qt::Key_ParenLeft: + _viewFrustumOffset.roll -= VIEW_FRUSTUM_OFFSET_DELTA; + break; + + case Qt::Key_ParenRight: + _viewFrustumOffset.roll += VIEW_FRUSTUM_OFFSET_DELTA; + break; + + case Qt::Key_Less: + _viewFrustumOffset.distance -= VIEW_FRUSTUM_OFFSET_DELTA; + break; + + case Qt::Key_Greater: + _viewFrustumOffset.distance += VIEW_FRUSTUM_OFFSET_DELTA; + break; + + case Qt::Key_Comma: + _viewFrustumOffset.up -= VIEW_FRUSTUM_OFFSET_UP_DELTA; + break; + + case Qt::Key_Period: + _viewFrustumOffset.up += VIEW_FRUSTUM_OFFSET_UP_DELTA; + break; + + default: + break; + } +} + +QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, + const QString actionName, + const QObject* receiver, + const char* member, + const QKeySequence& shortcut) { + QAction* action; + + if (receiver && member) { + action = destinationMenu->addAction(actionName, receiver, member, shortcut); + } else { + action = destinationMenu->addAction(actionName); + action->setShortcut(shortcut); + } + + _actionHash.insert(actionName, action); + + return action; +} + +QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, + const QString actionName, + const QObject* receiver, + const char* member, + const QKeySequence& shortcut, + const bool checked) { + QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, receiver, member, shortcut); + action->setCheckable(true); + action->setChecked(checked); + + return action; +} + +bool Menu::isOptionChecked(const QString& menuOption) { + return _actionHash.value(menuOption)->isChecked(); +} + +void Menu::triggerOption(const QString& menuOption) { + _actionHash.value(menuOption)->trigger(); +} + +QAction* Menu::getActionForOption(const QString& menuOption) { + return _actionHash.value(menuOption); +} + +bool Menu::isVoxelModeActionChecked() { + foreach (QAction* action, _voxelModeActionsGroup->actions()) { + if (action->isChecked()) { + return true; + } + } + return false; +} + +void Menu::editPreferences() { + Application *applicationInstance = Application::getInstance(); + QDialog dialog(applicationInstance->getGLWidget()); + dialog.setWindowTitle("Interface Preferences"); + QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom); + dialog.setLayout(layout); + + QFormLayout* form = new QFormLayout(); + layout->addLayout(form, 1); + + const int QLINE_MINIMUM_WIDTH = 400; + + QLineEdit* domainServerHostname = new QLineEdit(QString(NodeList::getInstance()->getDomainHostname())); + domainServerHostname->setMinimumWidth(QLINE_MINIMUM_WIDTH); + form->addRow("Domain server:", domainServerHostname); + + QLineEdit* avatarURL = new QLineEdit(applicationInstance->getAvatar()->getVoxels()->getVoxelURL().toString()); + avatarURL->setMinimumWidth(QLINE_MINIMUM_WIDTH); + form->addRow("Avatar URL:", avatarURL); + + QSpinBox* fieldOfView = new QSpinBox(); + fieldOfView->setMaximum(180); + fieldOfView->setMinimum(1); + fieldOfView->setValue(_fieldOfView); + form->addRow("Vertical Field of View (Degrees):", fieldOfView); + + QDoubleSpinBox* gyroCameraSensitivity = new QDoubleSpinBox(); + gyroCameraSensitivity->setValue(_gyroCameraSensitivity); + form->addRow("Gyro Camera Sensitivity (0 - 1):", gyroCameraSensitivity); + + QDoubleSpinBox* leanScale = new QDoubleSpinBox(); + leanScale->setValue(applicationInstance->getAvatar()->getLeanScale()); + form->addRow("Lean Scale:", leanScale); + + QSpinBox* audioJitterBufferSamples = new QSpinBox(); + audioJitterBufferSamples->setMaximum(10000); + audioJitterBufferSamples->setMinimum(-10000); + audioJitterBufferSamples->setValue(_audioJitterBufferSamples); + form->addRow("Audio Jitter Buffer Samples (0 for automatic):", audioJitterBufferSamples); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + dialog.connect(buttons, SIGNAL(accepted()), SLOT(accept())); + dialog.connect(buttons, SIGNAL(rejected()), SLOT(reject())); + layout->addWidget(buttons); + + if (dialog.exec() != QDialog::Accepted) { + return; + } + + QByteArray newHostname; + + if (domainServerHostname->text().size() > 0) { + // the user input a new hostname, use that + newHostname = domainServerHostname->text().toLocal8Bit(); + } else { + // the user left the field blank, use the default hostname + newHostname = QByteArray(DEFAULT_DOMAIN_HOSTNAME); + } + + // check if the domain server hostname is new + if (memcmp(NodeList::getInstance()->getDomainHostname(), newHostname.constData(), newHostname.size()) != 0) { + + NodeList::getInstance()->clear(); + + // kill the local voxels + applicationInstance->getVoxels()->killLocalVoxels(); + + // reset the environment to default + applicationInstance->getEnvironment()->resetToDefault(); + + // set the new hostname + NodeList::getInstance()->setDomainHostname(newHostname.constData()); + } + + QUrl url(avatarURL->text()); + applicationInstance->getAvatar()->getVoxels()->setVoxelURL(url); + Avatar::sendAvatarVoxelURLMessage(url); + + _gyroCameraSensitivity = gyroCameraSensitivity->value(); + + applicationInstance->getAvatar()->setLeanScale(leanScale->value()); + + _audioJitterBufferSamples = audioJitterBufferSamples->value(); + + if (_audioJitterBufferSamples != 0) { + applicationInstance->getAudio()->setJitterBufferSamples(_audioJitterBufferSamples); + } + + _fieldOfView = fieldOfView->value(); + applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height()); +} + + +void Menu::bandwidthDetails() { + + if (! _bandwidthDialog) { + _bandwidthDialog = new BandwidthDialog(Application::getInstance()->getGLWidget(), + Application::getInstance()->getBandwidthMeter()); + connect(_bandwidthDialog, SIGNAL(closed()), SLOT(bandwidthDetailsClosed())); + + _bandwidthDialog->show(); + } + _bandwidthDialog->raise(); +} + +void Menu::bandwidthDetailsClosed() { + delete _bandwidthDialog; + _bandwidthDialog = NULL; +} + +void Menu::cycleFrustumRenderMode() { + _frustumDrawMode = (FrustumDrawMode)((_frustumDrawMode + 1) % FRUSTUM_DRAW_MODE_COUNT); + updateFrustumRenderModeAction(); +} + +void Menu::chooseVoxelPaintColor() { + Application* appInstance = Application::getInstance(); + QAction* paintColor = _actionHash.value(MenuOption::VoxelPaintColor); + + QColor selected = QColorDialog::getColor(paintColor->data().value(), + appInstance->getGLWidget(), + "Voxel Paint Color"); + if (selected.isValid()) { + paintColor->setData(selected); + paintColor->setIcon(Swatch::createIcon(selected)); + } + + // restore the main window's active state +// _window->activateWindow(); +} + +void Menu::updateFrustumRenderModeAction() { + QAction* frustumRenderModeAction = _actionHash.value(MenuOption::FrustumRenderMode); + switch (_frustumDrawMode) { + default: + case FRUSTUM_DRAW_MODE_ALL: + frustumRenderModeAction->setText("Render Mode - All"); + break; + case FRUSTUM_DRAW_MODE_VECTORS: + frustumRenderModeAction->setText("Render Mode - Vectors"); + break; + case FRUSTUM_DRAW_MODE_PLANES: + frustumRenderModeAction->setText("Render Mode - Planes"); + break; + case FRUSTUM_DRAW_MODE_NEAR_PLANE: + frustumRenderModeAction->setText("Render Mode - Near"); + break; + case FRUSTUM_DRAW_MODE_FAR_PLANE: + frustumRenderModeAction->setText("Render Mode - Far"); + break; + case FRUSTUM_DRAW_MODE_KEYHOLE: + frustumRenderModeAction->setText("Render Mode - Keyhole"); + break; + } } \ No newline at end of file diff --git a/interface/src/Menu.h b/interface/src/Menu.h index d26a1a6879..56a7453dd8 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -9,11 +9,187 @@ #ifndef __hifi__Menu__ #define __hifi__Menu__ -class Menu { -public: - static void init(); -private: - static Menu* _instance; +#include +#include +#include + +enum FrustumDrawMode { + FRUSTUM_DRAW_MODE_ALL, + FRUSTUM_DRAW_MODE_VECTORS, + FRUSTUM_DRAW_MODE_PLANES, + FRUSTUM_DRAW_MODE_NEAR_PLANE, + FRUSTUM_DRAW_MODE_FAR_PLANE, + FRUSTUM_DRAW_MODE_KEYHOLE, + FRUSTUM_DRAW_MODE_COUNT }; +struct ViewFrustumOffset { + float yaw; + float pitch; + float roll; + float distance; + float up; +}; + +class Menu : public QMenuBar { + Q_OBJECT +public: + static Menu* getInstance(); + + bool isOptionChecked(const QString& menuOption); + void triggerOption(const QString& menuOption); + QAction* getActionForOption(const QString& menuOption); + bool isVoxelModeActionChecked(); + + float getAudioJitterBufferSamples() const { return _audioJitterBufferSamples; } + float getFieldOfView() const { return _fieldOfView; } + float getGyroCameraSensitivity() const { return _gyroCameraSensitivity; } + BandwidthDialog* getBandwidthDialog() const { return _bandwidthDialog; } + FrustumDrawMode getFrustumDrawMode() const { return _frustumDrawMode; } + ViewFrustumOffset getViewFrustumOffset() const { return _viewFrustumOffset; } + + void loadSettings(QSettings* settings = NULL); + void saveSettings(QSettings* settings = NULL); + void importSettings(); + void exportSettings(); + + void handleViewFrustumOffsetKeyModifier(int key); + +public slots: + void bandwidthDetails(); + +private slots: + void editPreferences(); + void bandwidthDetailsClosed(); + void cycleFrustumRenderMode(); + void chooseVoxelPaintColor(); + +private: + static Menu* _instance; + + Menu(); + + typedef void(*settingsAction)(QSettings*, QAction*); + static void loadAction(QSettings* set, QAction* action); + static void saveAction(QSettings* set, QAction* action); + void scanMenuBar(settingsAction modifySetting, QSettings* set); + void scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set); + + QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, + const QString actionName, + const QObject* receiver = NULL, + const char* member = NULL, + const QKeySequence& shortcut = 0); + QAction* addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, + const QString actionName, + const QObject* receiver = NULL, + const char* member = NULL, + const QKeySequence& shortcut = 0, + const bool checked = false); + + void updateFrustumRenderModeAction(); + + QHash _actionHash; + int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback + BandwidthDialog* _bandwidthDialog; + float _fieldOfView; /// in Degrees, doesn't apply to HMD like Oculus + FrustumDrawMode _frustumDrawMode; + float _gyroCameraSensitivity; + ViewFrustumOffset _viewFrustumOffset; + QActionGroup* _voxelModeActionsGroup; +}; + +namespace MenuOption { + + const QString Avatars = "Avatars"; + const QString AvatarAsBalls = "Avatar as Balls"; + const QString Atmosphere = "Atmosphere"; + const QString Bandwidth = "Bandwidth Display"; + const QString BandwidthDetails = "Bandwidth Details"; + const QString Collisions = "Collisions"; + const QString CopyVoxels = "Copy Voxels"; + const QString CoverageMap = "Render Coverage Map"; + const QString CoverageMapV2 = "Render Coverage Map V2"; + const QString CutVoxels = "Cut Voxels"; + const QString DecreaseAvatarSize = "Decrease Avatar Size"; + const QString DecreaseVoxelSize = "Decrease Voxel Size"; + const QString DestructiveAddVoxel = "Create Voxel is Destructive"; + const QString DisableDeltaSending = "Disable Delta Sending"; + const QString DisableLowRes = "Disable Lower Resolution While Moving"; + const QString DisableOcclusionCulling = "Disable Occlusion Culling"; + const QString DisplayFrustum = "Display Frustum"; + const QString EchoAudio = "Echo Audio"; + const QString ExportVoxels = "Export Voxels"; + const QString HeadMouse = "Head Mouse"; + const QString FaceMode = "Cycle Face Mode"; + const QString FalseColorByDistance = "FALSE Color By Distance"; + const QString FalseColorBySource = "FALSE Color By Source"; + const QString FalseColorEveryOtherVoxel = "FALSE Color Every Other Randomly"; + const QString FalseColorOccluded = "FALSE Color Occluded Voxels"; + const QString FalseColorOccludedV2 = "FALSE Color Occluded V2 Voxels"; + const QString FalseColorOutOfView = "FALSE Color Voxel Out of View"; + const QString FalseColorRandomly = "FALSE Color Voxels Randomly"; + const QString FirstPerson = "First Person"; + const QString FrameTimer = "Show Timer"; + const QString FrustumRenderMode = "Render Mode"; + const QString Fullscreen = "Fullscreen"; + const QString ImportVoxels = "Import Voxels"; + const QString ImportVoxelsClipboard = "Import Voxels to Clipboard"; + const QString IncreaseAvatarSize = "Increase Avatar Size"; + const QString IncreaseVoxelSize = "Increase Voxel Size"; + const QString KillLocalVoxels = "Kill Local Voxels"; + const QString GoHome = "Go Home"; + const QString Gravity = "Use Gravity"; + const QString GroundPlane = "Ground Plane"; + const QString GyroLook = "Smooth Gyro Look"; + const QString ListenModeNormal = "Listen Mode Normal"; + const QString ListenModePoint = "Listen Mode Point"; + const QString ListenModeSingleSource = "Listen Mode Single Source"; + const QString Log = "Log"; + const QString LookAtIndicator = "Look-at Indicator"; + const QString LookAtVectors = "Look-at Vectors"; + const QString LowPassFilter = "Low-pass Filter"; + const QString Mirror = "Mirror"; + const QString Monochrome = "Monochrome"; + const QString Noise = "Noise"; + const QString Oscilloscope = "Audio Oscilloscope"; + const QString Pair = "Pair"; + const QString ParticleSystem = "Particle System"; + const QString PasteVoxels = "Paste Voxels"; + const QString PipelineWarnings = "Show Render Pipeline Warnings"; + const QString Preferences = "Preferences..."; + const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors"; + const QString ResetAvatarSize = "Reset Avatar Size"; + const QString ResetSwatchColors = "Reset Swatch Colors"; + const QString RunTimingTests = "Run Timing Tests"; + const QString SettingsAutosave = "Autosave"; + const QString SettingsLoad = "Load Settings"; + const QString SettingsSave = "Save Settings"; + const QString SettingsImport = "Import Settings"; + const QString SettingsExport = "Export Settings"; + const QString ShowTrueColors = "Show TRUE Colors"; + const QString SimulateLeapHand = "Simulate Leap Hand"; + const QString SkeletonTracking = "Skeleton Tracking"; + const QString Stars = "Stars"; + const QString Stats = "Stats"; + const QString TestPing = "Test Ping"; + const QString TestRaveGlove = "Test Rave Glove"; + const QString TreeStats = "Calculate Tree Stats"; + const QString TransmitterDrive = "Transmitter Drive"; + const QString Quit = "Quit"; + const QString Webcam = "Webcam"; + const QString WebcamMode = "Cycle Webcam Send Mode"; + const QString WebcamTexture = "Webcam Texture"; + const QString Voxels = "Voxels"; + const QString VoxelAddMode = "Add Voxel Mode"; + const QString VoxelColorMode = "Color Voxel Mode"; + const QString VoxelDeleteMode = "Delete Voxel Mode"; + const QString VoxelGetColorMode = "Get Color Mode"; + const QString VoxelMode = "Cycle Voxel Mode"; + const QString VoxelPaintColor = "Voxel Paint Color"; + const QString VoxelSelectMode = "Select Voxel Mode"; + const QString VoxelStats = "Voxel Stats"; + const QString VoxelTextures = "Voxel Textures"; +} + #endif /* defined(__hifi__Menu__) */ diff --git a/interface/src/PairingHandler.cpp b/interface/src/PairingHandler.cpp index 920f2c519c..c220c5f943 100644 --- a/interface/src/PairingHandler.cpp +++ b/interface/src/PairingHandler.cpp @@ -17,6 +17,16 @@ const char PAIRING_SERVER_HOSTNAME[] = "pairing.highfidelity.io"; const int PAIRING_SERVER_PORT = 7247; +PairingHandler* PairingHandler::getInstance() { + static PairingHandler* instance = NULL; + + if (!instance) { + instance = new PairingHandler(); + } + + return instance; +} + void PairingHandler::sendPairRequest() { // grab the node socket from the NodeList singleton UDPSocket *nodeSocket = NodeList::getInstance()->getNodeSocket(); diff --git a/interface/src/PairingHandler.h b/interface/src/PairingHandler.h index d201465898..68d9f7de5a 100644 --- a/interface/src/PairingHandler.h +++ b/interface/src/PairingHandler.h @@ -9,11 +9,14 @@ #ifndef __hifi__PairingHandler__ #define __hifi__PairingHandler__ -#include +#include -class PairingHandler { +class PairingHandler : public QObject { + Q_OBJECT public: - static void sendPairRequest(); + static PairingHandler* getInstance(); +public slots: + void sendPairRequest(); }; #endif /* defined(__hifi__PairingHandler__) */ diff --git a/interface/src/Swatch.cpp b/interface/src/Swatch.cpp index ea8516e9a8..d3d2a2341b 100644 --- a/interface/src/Swatch.cpp +++ b/interface/src/Swatch.cpp @@ -1,6 +1,20 @@ +// +// Swatch.h +// interface +// +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + #include "Swatch.h" #include +QIcon Swatch::createIcon(const QColor& color) { + QPixmap map(16, 16); + map.fill(color); + return QIcon(map); +} + + Swatch::Swatch(QAction* action) : Tool(action, 0, -1, -1), _textRenderer(MONO_FONT_FAMILY, 10, 100), diff --git a/interface/src/Swatch.h b/interface/src/Swatch.h index e167cd05c4..e1245fb4f3 100644 --- a/interface/src/Swatch.h +++ b/interface/src/Swatch.h @@ -23,6 +23,8 @@ static const int colorBase[8][3] = {{237, 175, 0}, class Swatch : public Tool { public: + static QIcon createIcon(const QColor& color); + Swatch(QAction* action); QColor getColor(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index e561720eaf..714c789487 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -63,6 +63,28 @@ bool usingBigSphereCollisionTest = true; float chatMessageScale = 0.0015; float chatMessageHeight = 0.20; +void Avatar::sendAvatarVoxelURLMessage(const QUrl& url) { + uint16_t ownerID = NodeList::getInstance()->getOwnerID(); + + if (ownerID == UNKNOWN_NODE_ID) { + return; // we don't yet know who we are + } + + QByteArray message; + + char packetHeader[MAX_PACKET_HEADER_BYTES]; + int numBytesPacketHeader = populateTypeAndVersion((unsigned char*) packetHeader, PACKET_TYPE_AVATAR_VOXEL_URL); + + message.append(packetHeader, numBytesPacketHeader); + message.append((const char*)&ownerID, sizeof(ownerID)); + message.append(url.toEncoded()); + + Application::controlledBroadcastToNodes((unsigned char*)message.data(), + message.size(), + &NODE_TYPE_AVATAR_MIXER, + 1); +} + Avatar::Avatar(Node* owningNode) : AvatarData(owningNode), _initialized(false), diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 29a79047ed..bb2a460bdf 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -123,6 +123,8 @@ enum ScreenTintLayer class Avatar : public AvatarData { public: + static void sendAvatarVoxelURLMessage(const QUrl& url); + Avatar(Node* owningNode = NULL); ~Avatar(); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index f35b09895e..160510734f 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -384,6 +384,12 @@ void Head::renderMohawk() { if (!_mohawkTriangleFan) { createMohawk(); + + // if we get here and still don't have a mohawk then we don't know who we are + // so return out since we can't render it yet + if (!_mohawkTriangleFan) { + return; + } } if (USING_PHYSICAL_MOHAWK) {