diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 744d16b58e..7cf3bdd056 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -417,8 +417,8 @@ int DomainServer::civetwebRequestHandler(struct mg_connection *connection) { mg_printf(connection, "%s", RESPONSE_200); // we have a valid UUID and node - kill the node that has this assignment - NodeList::getInstance()->killNodeWithUUID(deleteUUID); - + QMetaObject::invokeMethod(NodeList::getInstance(), "killNodeWithUUID", Q_ARG(const QUuid&, deleteUUID)); + // successfully processed request return 1; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cb60116b37..d7c7f103db 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1432,8 +1432,10 @@ void Application::terminate() { _voxelHideShowThread.terminate(); _voxelEditSender.terminate(); _particleEditSender.terminate(); - _persistThread->terminate(); - _persistThread = NULL; + if (_persistThread) { + _persistThread->terminate(); + _persistThread = NULL; + } } static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& dataBytes) { @@ -2276,6 +2278,7 @@ void Application::updateMouseVoxels(float deltaTime, glm::vec3& mouseRayOrigin, } } + void Application::updateHandAndTouch(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateHandAndTouch()"); @@ -2319,7 +2322,9 @@ void Application::updateThreads(float deltaTime) { _voxelHideShowThread.threadRoutine(); _voxelEditSender.threadRoutine(); _particleEditSender.threadRoutine(); - _persistThread->threadRoutine(); + if (_persistThread) { + _persistThread->threadRoutine(); + } } } @@ -4395,10 +4400,13 @@ void Application::updateLocalOctreeCache(bool firstTime) { QString localVoxelCacheFileName = getLocalVoxelCacheFileName(); const int LOCAL_CACHE_PERSIST_INTERVAL = 1000 * 10; // every 10 seconds - _persistThread = new OctreePersistThread(_voxels.getTree(), - localVoxelCacheFileName.toLocal8Bit().constData(),LOCAL_CACHE_PERSIST_INTERVAL); - qDebug() << "updateLocalOctreeCache()... localVoxelCacheFileName=" << localVoxelCacheFileName; + if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableLocalVoxelCache)) { + _persistThread = new OctreePersistThread(_voxels.getTree(), + localVoxelCacheFileName.toLocal8Bit().constData(),LOCAL_CACHE_PERSIST_INTERVAL); + + qDebug() << "updateLocalOctreeCache()... localVoxelCacheFileName=" << localVoxelCacheFileName; + } if (_persistThread) { _voxels.beginLoadingLocalVoxelCache(); // while local voxels are importing, don't do individual node VBO updates diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 68d82efe1d..8989906b99 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -37,18 +37,18 @@ Menu* Menu::_instance = NULL; Menu* Menu::getInstance() { static QMutex menuInstanceMutex; - + // lock the menu instance mutex to make sure we don't race and create two menus and crash menuInstanceMutex.lock(); - + if (!_instance) { qDebug("First call to Menu::getInstance() - initing menu."); - + _instance = new Menu(); } - + menuInstanceMutex.unlock(); - + return _instance; } @@ -72,9 +72,9 @@ Menu::Menu() : _maxVoxelPacketsPerSecond(DEFAULT_MAX_VOXEL_PPS) { Application *appInstance = Application::getInstance(); - + QMenu* fileMenu = addMenu("File"); - + #ifdef Q_OS_MAC addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, @@ -83,7 +83,7 @@ Menu::Menu() : SLOT(aboutApp()), QAction::AboutRole); #endif - + (addActionToQMenuAndActionHash(fileMenu, MenuOption::Login, 0, @@ -97,7 +97,7 @@ Menu::Menu() : addDisabledActionAndSeparator(fileMenu, "Voxels"); addActionToQMenuAndActionHash(fileMenu, MenuOption::ExportVoxels, Qt::CTRL | Qt::Key_E, appInstance, SLOT(exportVoxels())); addActionToQMenuAndActionHash(fileMenu, MenuOption::ImportVoxels, Qt::CTRL | Qt::Key_I, appInstance, SLOT(importVoxels())); - + addDisabledActionAndSeparator(fileMenu, "Go"); addActionToQMenuAndActionHash(fileMenu, MenuOption::GoHome, @@ -120,45 +120,45 @@ Menu::Menu() : this, SLOT(goToUser())); - + addDisabledActionAndSeparator(fileMenu, "Settings"); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings())); addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings())); - + addDisabledActionAndSeparator(fileMenu, "Devices"); addActionToQMenuAndActionHash(fileMenu, MenuOption::Pair, 0, PairingHandler::getInstance(), SLOT(sendPairRequest())); addCheckableActionToQMenuAndActionHash(fileMenu, MenuOption::TransmitterDrive, 0, true); - + addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, appInstance, SLOT(quit()), QAction::QuitRole); - - + + QMenu* editMenu = addMenu("Edit"); - + addActionToQMenuAndActionHash(editMenu, MenuOption::Preferences, Qt::CTRL | Qt::Key_Comma, this, SLOT(editPreferences()), QAction::PreferencesRole); - + addDisabledActionAndSeparator(editMenu, "Voxels"); - + addActionToQMenuAndActionHash(editMenu, MenuOption::CutVoxels, Qt::CTRL | Qt::Key_X, appInstance, SLOT(cutVoxels())); addActionToQMenuAndActionHash(editMenu, MenuOption::CopyVoxels, Qt::CTRL | Qt::Key_C, appInstance, SLOT(copyVoxels())); addActionToQMenuAndActionHash(editMenu, MenuOption::PasteVoxels, Qt::CTRL | Qt::Key_V, appInstance, SLOT(pasteVoxels())); addActionToQMenuAndActionHash(editMenu, MenuOption::NudgeVoxels, Qt::CTRL | Qt::Key_N, appInstance, SLOT(nudgeVoxels())); - + #ifdef __APPLE__ addActionToQMenuAndActionHash(editMenu, MenuOption::DeleteVoxels, Qt::Key_Backspace, appInstance, SLOT(deleteVoxels())); #else addActionToQMenuAndActionHash(editMenu, MenuOption::DeleteVoxels, Qt::Key_Delete, appInstance, SLOT(deleteVoxels())); #endif - + addDisabledActionAndSeparator(editMenu, "Physics"); addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, true); addCheckableActionToQMenuAndActionHash(editMenu, @@ -167,47 +167,47 @@ Menu::Menu() : true, appInstance->getAvatar(), SLOT(setWantCollisionsOn(bool))); - + QMenu* toolsMenu = addMenu("Tools"); - + _voxelModeActionsGroup = new QActionGroup(this); _voxelModeActionsGroup->setExclusive(false); - + QAction* addVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelAddMode, Qt::Key_V); _voxelModeActionsGroup->addAction(addVoxelMode); - + QAction* deleteVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelDeleteMode, Qt::Key_R); _voxelModeActionsGroup->addAction(deleteVoxelMode); - + QAction* colorVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelColorMode, Qt::Key_B); _voxelModeActionsGroup->addAction(colorVoxelMode); - + QAction* selectVoxelMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelSelectMode, Qt::Key_O); _voxelModeActionsGroup->addAction(selectVoxelMode); - + QAction* getColorMode = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelGetColorMode, Qt::Key_G); _voxelModeActionsGroup->addAction(getColorMode); - + addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::ClickToFly); - - + + // connect each of the voxel mode actions to the updateVoxelModeActionsSlot foreach (QAction* action, _voxelModeActionsGroup->actions()) { connect(action, SIGNAL(triggered()), this, SLOT(updateVoxelModeActions())); } - + QAction* voxelPaintColor = addActionToQMenuAndActionHash(toolsMenu, MenuOption::VoxelPaintColor, Qt::META | Qt::Key_C, this, SLOT(chooseVoxelPaintColor())); - + Application::getInstance()->getSwatch()->setAction(voxelPaintColor); - + QColor paintColor(128, 128, 128); voxelPaintColor->setData(paintColor); voxelPaintColor->setIcon(Swatch::createIcon(paintColor)); - + addActionToQMenuAndActionHash(toolsMenu, MenuOption::DecreaseVoxelSize, QKeySequence::ZoomOut, @@ -220,9 +220,9 @@ Menu::Menu() : SLOT(increaseVoxelSize())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::ResetSwatchColors, 0, this, SLOT(resetSwatchColors())); - + QMenu* viewMenu = addMenu("View"); - + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Fullscreen, Qt::CTRL | Qt::META | Qt::Key_F, @@ -233,10 +233,10 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, false); - - + + QMenu* avatarSizeMenu = viewMenu->addMenu("Avatar Size"); - + addActionToQMenuAndActionHash(avatarSizeMenu, MenuOption::IncreaseAvatarSize, Qt::Key_Plus, @@ -263,8 +263,8 @@ Menu::Menu() : true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MoveWithLean, 0, false); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HeadMouse, 0, false); - - + + addDisabledActionAndSeparator(viewMenu, "Stats"); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash); addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, appInstance, SLOT(toggleLogDialog())); @@ -272,7 +272,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Bandwidth, 0, true); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, this, SLOT(bandwidthDetails())); addActionToQMenuAndActionHash(viewMenu, MenuOption::VoxelStats, 0, this, SLOT(voxelStatsDetails())); - + QMenu* developerMenu = addMenu("Developer"); QMenu* renderOptionsMenu = developerMenu->addMenu("Rendering Options"); @@ -284,11 +284,11 @@ Menu::Menu() : 0, appInstance->getGlowEffect(), SLOT(cycleRenderMode())); - + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ParticleCloud, 0, false); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, false); - + QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options"); @@ -301,19 +301,20 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontRenderVoxels); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontCallOpenGLForVoxels); - _useVoxelShader = addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::UseVoxelShader, 0, + _useVoxelShader = addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::UseVoxelShader, 0, false, appInstance->getVoxels(), SLOT(setUseVoxelShader(bool))); - addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelsAsPoints, 0, + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelsAsPoints, 0, false, appInstance->getVoxels(), SLOT(setVoxelsAsPoints(bool))); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges); addActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools())); + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DisableLocalVoxelCache); QMenu* voxelProtoOptionsMenu = voxelOptionsMenu->addMenu("Voxel Server Protocol Options"); - + addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DisableColorVoxels); addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DisableLowRes); addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DisableDeltaSending); @@ -322,16 +323,16 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(voxelProtoOptionsMenu, MenuOption::DestructiveAddVoxel); QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options"); - + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollisionProxies); - + addActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::FaceMode, 0, &appInstance->getAvatar()->getHead().getVideoFace(), SLOT(cycleRenderMode())); - + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtIndicator, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, @@ -378,12 +379,12 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::SimulateLeapHand); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayLeapHands, 0, true); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::LeapDrive, 0, false); - addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::BallFromHand, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::VoxelDrumming, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false); - - + + QMenu* trackingOptionsMenu = developerMenu->addMenu("Tracking Options"); addCheckableActionToQMenuAndActionHash(trackingOptionsMenu, @@ -402,7 +403,6 @@ Menu::Menu() : SLOT(setLEDTrackingOn(bool))); #endif //def HAVE_LIBVPX - addDisabledActionAndSeparator(developerMenu, "Testing"); QMenu* timingMenu = developerMenu->addMenu("Timing and Statistics Tools"); @@ -414,7 +414,7 @@ Menu::Menu() : Qt::SHIFT | Qt::Key_S, appInstance->getVoxels(), SLOT(collectStatsForTreesAndVBOs())); - + QMenu* frustumMenu = developerMenu->addMenu("View Frustum Debugging Tools"); addCheckableActionToQMenuAndActionHash(frustumMenu, MenuOption::DisplayFrustum, Qt::SHIFT | Qt::Key_F); addActionToQMenuAndActionHash(frustumMenu, @@ -423,8 +423,8 @@ Menu::Menu() : this, SLOT(cycleFrustumRenderMode())); updateFrustumRenderModeAction(); - - + + QMenu* renderDebugMenu = developerMenu->addMenu("Render Debugging Tools"); addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::PipelineWarnings, Qt::CTRL | Qt::SHIFT | Qt::Key_P); addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::SuppressShortTimings, Qt::CTRL | Qt::SHIFT | Qt::Key_S); @@ -437,48 +437,48 @@ Menu::Menu() : Qt::CTRL | Qt::Key_A, appInstance->getVoxels(), SLOT(showAllLocalVoxels())); - + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::KillLocalVoxels, Qt::CTRL | Qt::Key_K, appInstance, SLOT(doKillLocalVoxels())); - + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::RandomizeVoxelColors, Qt::CTRL | Qt::Key_R, appInstance->getVoxels(), SLOT(randomizeVoxelColors())); - + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorRandomly, 0, appInstance->getVoxels(), SLOT(falseColorizeRandom())); - + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorEveryOtherVoxel, 0, appInstance->getVoxels(), SLOT(falseColorizeRandomEveryOther())); - + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorByDistance, 0, appInstance->getVoxels(), SLOT(falseColorizeDistanceFromView())); - + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorOutOfView, 0, appInstance->getVoxels(), SLOT(falseColorizeInView())); - + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorBySource, 0, appInstance->getVoxels(), SLOT(falseColorizeBySource())); - + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::ShowTrueColors, Qt::CTRL | Qt::Key_T, @@ -491,32 +491,32 @@ Menu::Menu() : 0, appInstance->getVoxels(), SLOT(falseColorizeOccluded())); - + addActionToQMenuAndActionHash(renderDebugMenu, MenuOption::FalseColorOccludedV2, 0, appInstance->getVoxels(), SLOT(falseColorizeOccludedV2())); - + addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::CoverageMap, Qt::SHIFT | Qt::CTRL | Qt::Key_O); addCheckableActionToQMenuAndActionHash(renderDebugMenu, MenuOption::CoverageMapV2, Qt::SHIFT | Qt::CTRL | Qt::Key_P); - + QMenu* audioDebugMenu = developerMenu->addMenu("Audio Debugging Tools"); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio); - addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel, - Qt::CTRL | Qt::SHIFT | Qt::Key_V, + addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel, + Qt::CTRL | Qt::SHIFT | Qt::Key_V, this, SLOT(pasteToVoxel())); - - + + #ifndef Q_OS_MAC QMenu* helpMenu = addMenu("Help"); QAction* helpAction = helpMenu->addAction(MenuOption::AboutApp); connect(helpAction, SIGNAL(triggered()), this, SLOT(aboutApp())); #endif - + } Menu::~Menu() { @@ -528,7 +528,7 @@ void Menu::loadSettings(QSettings* settings) { if (!settings) { settings = Application::getInstance()->getSettings(); } - + _audioJitterBufferSamples = loadSetting(settings, "audioJitterBufferSamples", 0); _fieldOfView = loadSetting(settings, "fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES); _faceshiftEyeDeflection = loadSetting(settings, "faceshiftEyeDeflection", DEFAULT_FACESHIFT_EYE_DEFLECTION); @@ -536,7 +536,7 @@ void Menu::loadSettings(QSettings* settings) { _maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS); _voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_OCTREE_SIZE_SCALE); _boundaryLevelAdjust = loadSetting(settings, "boundaryLevelAdjust", 0); - + settings->beginGroup("View Frustum Offset Camera"); // in case settings is corrupt or missing loadSetting() will check for NaN _viewFrustumOffset.yaw = loadSetting(settings, "viewFrustumOffsetYaw", 0.0f); @@ -545,7 +545,7 @@ void Menu::loadSettings(QSettings* settings) { _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); @@ -558,7 +558,7 @@ void Menu::saveSettings(QSettings* settings) { if (!settings) { settings = Application::getInstance()->getSettings(); } - + settings->setValue("audioJitterBufferSamples", _audioJitterBufferSamples); settings->setValue("fieldOfView", _fieldOfView); settings->setValue("faceshiftEyeDeflection", _faceshiftEyeDeflection); @@ -573,7 +573,7 @@ void Menu::saveSettings(QSettings* settings) { 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); @@ -618,7 +618,7 @@ void Menu::saveAction(QSettings* set, QAction* action) { 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); } @@ -626,7 +626,7 @@ void Menu::scanMenuBar(settingsAction modifySetting, QSettings* 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()) { @@ -642,48 +642,48 @@ void Menu::scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set) { 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; } @@ -701,7 +701,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, const char* member, QAction::MenuRole role) { QAction* action; - + if (receiver && member) { action = destinationMenu->addAction(actionName, receiver, member, shortcut); } else { @@ -709,9 +709,9 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, action->setShortcut(shortcut); } action->setMenuRole(role); - + _actionHash.insert(actionName, action); - + return action; } @@ -724,7 +724,7 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member); action->setCheckable(true); action->setChecked(checked); - + return action; } @@ -761,7 +761,7 @@ void Menu::aboutApp() { void sendFakeEnterEvent() { QPoint lastCursorPosition = QCursor::pos(); QGLWidget* glWidget = Application::getInstance()->getGLWidget(); - + QPoint windowPosition = glWidget->mapFromGlobal(lastCursorPosition); QEnterEvent enterEvent = QEnterEvent(windowPosition, windowPosition, lastCursorPosition); QCoreApplication::sendEvent(glWidget, &enterEvent); @@ -778,62 +778,62 @@ void Menu::login() { loginDialog.setTextValue(username); loginDialog.setWindowFlags(Qt::Sheet); loginDialog.resize(loginDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, loginDialog.size().height()); - + int dialogReturn = loginDialog.exec(); - + if (dialogReturn == QDialog::Accepted && !loginDialog.textValue().isEmpty() && loginDialog.textValue() != username) { // there has been a username change // ask for a profile reset with the new username Application::getInstance()->resetProfile(loginDialog.textValue()); - + } - + sendFakeEnterEvent(); } void Menu::editPreferences() { Application* applicationInstance = Application::getInstance(); - + QDialog dialog(applicationInstance->getWindow()); dialog.setWindowTitle("Interface Preferences"); QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom); dialog.setLayout(layout); - + QFormLayout* form = new QFormLayout(); layout->addLayout(form, 1); - + QString faceURLString = applicationInstance->getProfile()->getFaceModelURL().toString(); QLineEdit* faceURLEdit = new QLineEdit(faceURLString); faceURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH); form->addRow("Face URL:", faceURLEdit); - + QString skeletonURLString = applicationInstance->getProfile()->getSkeletonModelURL().toString(); QLineEdit* skeletonURLEdit = new QLineEdit(skeletonURLString); skeletonURLEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH); form->addRow("Skeleton URL:", skeletonURLEdit); - + QSlider* pupilDilation = new QSlider(Qt::Horizontal); pupilDilation->setValue(applicationInstance->getAvatar()->getHead().getPupilDilation() * pupilDilation->maximum()); form->addRow("Pupil Dilation:", pupilDilation); - + QSlider* faceshiftEyeDeflection = new QSlider(Qt::Horizontal); faceshiftEyeDeflection->setValue(_faceshiftEyeDeflection * faceshiftEyeDeflection->maximum()); form->addRow("Faceshift Eye Deflection:", faceshiftEyeDeflection); - + QSpinBox* fieldOfView = new QSpinBox(); fieldOfView->setMaximum(180); fieldOfView->setMinimum(1); fieldOfView->setValue(_fieldOfView); form->addRow("Vertical Field of View (Degrees):", fieldOfView); - + QDoubleSpinBox* leanScale = new QDoubleSpinBox(); leanScale->setValue(applicationInstance->getAvatar()->getLeanScale()); form->addRow("Lean Scale:", leanScale); - + QDoubleSpinBox* avatarScale = new QDoubleSpinBox(); avatarScale->setValue(applicationInstance->getAvatar()->getScale()); form->addRow("Avatar Scale:", avatarScale); - + QSpinBox* audioJitterBufferSamples = new QSpinBox(); audioJitterBufferSamples->setMaximum(10000); audioJitterBufferSamples->setMinimum(-10000); @@ -859,93 +859,93 @@ void Menu::editPreferences() { maxVoxelsPPS->setSingleStep(STEP_MAX_VOXELS_PPS); maxVoxelsPPS->setValue(_maxVoxelPacketsPerSecond); form->addRow("Maximum Voxels Packets Per Second:", maxVoxelsPPS); - + 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); - + int ret = dialog.exec(); if (ret == QDialog::Accepted) { QUrl faceModelURL(faceURLEdit->text()); - + if (faceModelURL.toString() != faceURLString) { // change the faceModelURL in the profile, it will also update this user's BlendFace applicationInstance->getProfile()->setFaceModelURL(faceModelURL); - + // send the new face mesh URL to the data-server (if we have a client UUID) DataServerClient::putValueForKey(DataServerKey::FaceMeshURL, faceModelURL.toString().toLocal8Bit().constData()); } - + QUrl skeletonModelURL(skeletonURLEdit->text()); - + if (skeletonModelURL.toString() != skeletonURLString) { // change the skeletonModelURL in the profile, it will also update this user's Body applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL); - + // send the new skeleton model URL to the data-server (if we have a client UUID) DataServerClient::putValueForKey(DataServerKey::SkeletonURL, skeletonModelURL.toString().toLocal8Bit().constData()); } - + applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum()); - + _maxVoxels = maxVoxels->value(); applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels); _maxVoxelPacketsPerSecond = maxVoxelsPPS->value(); - + applicationInstance->getAvatar()->setLeanScale(leanScale->value()); applicationInstance->getAvatar()->setClampedTargetScale(avatarScale->value()); - + _audioJitterBufferSamples = audioJitterBufferSamples->value(); - + if (_audioJitterBufferSamples != 0) { applicationInstance->getAudio()->setJitterBufferSamples(_audioJitterBufferSamples); } - + _fieldOfView = fieldOfView->value(); applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height()); - + _faceshiftEyeDeflection = faceshiftEyeDeflection->value() / (float)faceshiftEyeDeflection->maximum(); } - + sendFakeEnterEvent(); } void Menu::goToDomain() { - + QString currentDomainHostname = NodeList::getInstance()->getDomainHostname(); - + if (NodeList::getInstance()->getDomainPort() != DEFAULT_DOMAIN_SERVER_PORT) { // add the port to the currentDomainHostname string if it is custom currentDomainHostname.append(QString(":%1").arg(NodeList::getInstance()->getDomainPort())); } - + QInputDialog domainDialog(Application::getInstance()->getWindow()); domainDialog.setWindowTitle("Go to Domain"); domainDialog.setLabelText("Domain server:"); domainDialog.setTextValue(currentDomainHostname); domainDialog.setWindowFlags(Qt::Sheet); domainDialog.resize(domainDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, domainDialog.size().height()); - + int dialogReturn = domainDialog.exec(); if (dialogReturn == QDialog::Accepted) { QString newHostname(DEFAULT_DOMAIN_HOSTNAME); - + if (domainDialog.textValue().size() > 0) { // the user input a new hostname, use that newHostname = domainDialog.textValue(); } - + // send a node kill request, indicating to other clients that they should play the "disappeared" effect NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); - + // give our nodeList the new domain-server hostname NodeList::getInstance()->setDomainHostname(domainDialog.textValue()); } - + sendFakeEnterEvent(); } @@ -954,8 +954,8 @@ void Menu::goToLocation() { glm::vec3 avatarPos = myAvatar->getPosition(); QString currentLocation = QString("%1, %2, %3").arg(QString::number(avatarPos.x), QString::number(avatarPos.y), QString::number(avatarPos.z)); - - + + QInputDialog coordinateDialog(Application::getInstance()->getWindow()); coordinateDialog.setWindowTitle("Go to Location"); coordinateDialog.setLabelText("Coordinate as x,y,z:"); @@ -966,10 +966,10 @@ void Menu::goToLocation() { int dialogReturn = coordinateDialog.exec(); if (dialogReturn == QDialog::Accepted && !coordinateDialog.textValue().isEmpty()) { QByteArray newCoordinates; - + QString delimiterPattern(","); QStringList coordinateItems = coordinateDialog.textValue().split(delimiterPattern); - + const int NUMBER_OF_COORDINATE_ITEMS = 3; const int X_ITEM = 0; const int Y_ITEM = 1; @@ -979,17 +979,17 @@ void Menu::goToLocation() { double y = coordinateItems[Y_ITEM].toDouble(); double z = coordinateItems[Z_ITEM].toDouble(); glm::vec3 newAvatarPos(x, y, z); - + if (newAvatarPos != avatarPos) { // send a node kill request, indicating to other clients that they should play the "disappeared" effect NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); - + qDebug("Going To Location: %f, %f, %f...", x, y, z); - myAvatar->setPosition(newAvatarPos); + myAvatar->setPosition(newAvatarPos); } } } - + sendFakeEnterEvent(); } @@ -1001,7 +1001,7 @@ void Menu::goToUser() { userDialog.setTextValue(username); userDialog.setWindowFlags(Qt::Sheet); userDialog.resize(userDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, userDialog.size().height()); - + int dialogReturn = userDialog.exec(); if (dialogReturn == QDialog::Accepted && !userDialog.textValue().isEmpty()) { // there's a username entered by the user, make a request to the data-server @@ -1009,7 +1009,7 @@ void Menu::goToUser() { QStringList() << DataServerKey::Domain << DataServerKey::Position << DataServerKey::Orientation, userDialog.textValue()); } - + sendFakeEnterEvent(); } @@ -1020,15 +1020,15 @@ void Menu::pasteToVoxel() { QString octalCode = ""; pasteToOctalCodeDialog.setTextValue(octalCode); pasteToOctalCodeDialog.setWindowFlags(Qt::Sheet); - pasteToOctalCodeDialog.resize(pasteToOctalCodeDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, + pasteToOctalCodeDialog.resize(pasteToOctalCodeDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, pasteToOctalCodeDialog.size().height()); - + int dialogReturn = pasteToOctalCodeDialog.exec(); if (dialogReturn == QDialog::Accepted && !pasteToOctalCodeDialog.textValue().isEmpty()) { // we got an octalCode to paste to... QString locationToPaste = pasteToOctalCodeDialog.textValue(); unsigned char* octalCodeDestination = hexStringToOctalCode(locationToPaste); - + // check to see if it was a legit octcode... if (locationToPaste == octalCodeToHexString(octalCodeDestination)) { Application::getInstance()->pasteVoxelsToOctalCode(octalCodeDestination); @@ -1036,7 +1036,7 @@ void Menu::pasteToVoxel() { qDebug() << "Problem with octcode..."; } } - + sendFakeEnterEvent(); } @@ -1045,7 +1045,7 @@ void Menu::bandwidthDetails() { _bandwidthDialog = new BandwidthDialog(Application::getInstance()->getGLWidget(), Application::getInstance()->getBandwidthMeter()); connect(_bandwidthDialog, SIGNAL(closed()), SLOT(bandwidthDetailsClosed())); - + _bandwidthDialog->show(); } _bandwidthDialog->raise(); @@ -1118,7 +1118,7 @@ void Menu::updateVoxelModeActions() { 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"); @@ -1126,7 +1126,7 @@ void Menu::chooseVoxelPaintColor() { paintColor->setData(selected); paintColor->setIcon(Swatch::createIcon(selected)); } - + // restore the main window's active state appInstance->getWindow()->activateWindow(); } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index ca45f9000e..a49657e933 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -44,12 +44,12 @@ class Menu : public QMenuBar, public AbstractMenuInterface { public: static Menu* getInstance(); ~Menu(); - + 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 getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; } @@ -61,7 +61,7 @@ public: int getMaxVoxels() const { return _maxVoxels; } QAction* getUseVoxelShader() const { return _useVoxelShader; } - + void handleViewFrustumOffsetKeyModifier(int key); // User Tweakable LOD Items @@ -69,10 +69,10 @@ public: float getVoxelSizeScale() const { return _voxelSizeScale; } void setBoundaryLevelAdjust(int boundaryLevelAdjust); int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } - + // User Tweakable PPS from Voxel Server int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPacketsPerSecond; } - + virtual QMenu* getActiveScriptsMenu() { return _activeScriptsMenu;} virtual QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, const QString actionName, @@ -81,7 +81,7 @@ public: const char* member = NULL, QAction::MenuRole role = QAction::NoRole); virtual void removeAction(QMenu* menu, const QString& actionName); - + public slots: void bandwidthDetails(); void voxelStatsDetails(); @@ -92,7 +92,7 @@ public slots: void exportSettings(); void goToUser(); void pasteToVoxel(); - + private slots: void aboutApp(); void login(); @@ -107,30 +107,30 @@ private slots: void chooseVoxelPaintColor(); void runTests(); void resetSwatchColors(); - + 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); - + /// helper method to have separators with labels that are also compatible with OS X void addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName); - + QAction* addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, const QString actionName, const QKeySequence& shortcut = 0, const bool checked = false, const QObject* receiver = NULL, const char* member = NULL); - + void updateFrustumRenderModeAction(); - + QHash _actionHash; int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback BandwidthDialog* _bandwidthDialog; @@ -171,6 +171,7 @@ namespace MenuOption { const QString DestructiveAddVoxel = "Create Voxel is Destructive"; const QString DisableColorVoxels = "Disable Colored Voxels"; const QString DisableDeltaSending = "Disable Delta Sending"; + const QString DisableLocalVoxelCache = "Disable Local Voxel Cache"; const QString DisableLowRes = "Disable Lower Resolution While Moving"; const QString DisplayFrustum = "Display Frustum"; const QString DisplayLeapHands = "Display Leap Hands"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b9383474dd..2eb85dbea2 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -355,6 +355,55 @@ bool Avatar::findSpherePenetration(const glm::vec3& penetratorCenter, float pene return false; } +bool Avatar::findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) { + // TODO: provide an early exit using bounding sphere of entire avatar + + const HandData* handData = getHandData(); + if (handData) { + int jointIndices[2] = { _skeletonModel.getLeftHandJointIndex(), _skeletonModel.getRightHandJointIndex() }; + for (int i = 0; i < 2; i++) { + const PalmData* palm = handData->getPalm(i); + if (palm) { + // create a disk collision proxy where the hand is + glm::vec3 fingerAxis(0.f); + for (size_t f = 0; f < palm->getNumFingers(); ++f) { + const FingerData& finger = (palm->getFingers())[f]; + if (finger.isActive()) { + // compute finger axis + glm::vec3 fingerTip = finger.getTipPosition(); + glm::vec3 fingerRoot = finger.getRootPosition(); + fingerAxis = glm::normalize(fingerTip - fingerRoot); + break; + } + } + glm::vec3 handPosition; + if (i == 0) { + _skeletonModel.getLeftHandPosition(handPosition); + } + else { + _skeletonModel.getRightHandPosition(handPosition); + } + glm::vec3 diskCenter = handPosition + HAND_PADDLE_OFFSET * fingerAxis; + glm::vec3 diskNormal = palm->getNormal(); + + // collide against the disk + if (findSphereDiskPenetration(sphereCenter, sphereRadius, + diskCenter, HAND_PADDLE_RADIUS, diskNormal, collision._penetration)) { + collision._addedVelocity = palm->getVelocity(); + return true; + } + } + } + } + + if (_skeletonModel.findSpherePenetration(sphereCenter, sphereRadius, collision._penetration)) { + collision._penetration /= (float)(TREE_SCALE); + collision._addedVelocity = getVelocity(); + return true; + } + return false; +} + int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) { // change in position implies movement glm::vec3 oldPosition = _position; diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index da93ff6761..7bbcf63cab 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -167,6 +167,13 @@ public: bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, int skeletonSkipIndex = -1) const; + /// Checks for collision between the a sphere and the avatar. + /// \param collisionCenter the center of the penetration test sphere + /// \param collisionRadius the radius of the penetration test sphere + /// \param collision[out] the details of the collision point + /// \return whether or not the sphere collided + virtual bool findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision); + virtual int parseData(unsigned char* sourceBuffer, int numBytes); static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index ef6b97d6cd..0b399c4287 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -616,23 +616,37 @@ void Hand::renderLeapHands(bool isMine) { } } - // Draw the palms - for (size_t i = 0; i < getNumPalms(); ++i) { - PalmData& palm = getPalms()[i]; - if (palm.isActive()) { - const float palmThickness = 0.02f; - if (palm.getIsCollidingWithPalm()) { - glColor4f(1, 0, 0, 0.50); - } else { - glColor4f(handColor.r, handColor.g, handColor.b, 0.25); + // Draw the hand paddles + int MAX_NUM_PADDLES = 2; // one for left and one for right + glColor4f(handColor.r, handColor.g, handColor.b, 0.3f); + for (int i = 0; i < MAX_NUM_PADDLES; i++) { + const PalmData* palm = getPalm(i); + if (palm) { + // compute finger axis + glm::vec3 fingerAxis(0.f); + for (size_t f = 0; f < palm->getNumFingers(); ++f) { + const FingerData& finger = (palm->getFingers())[f]; + if (finger.isActive()) { + glm::vec3 fingerTip = finger.getTipPosition(); + glm::vec3 fingerRoot = finger.getRootPosition(); + fingerAxis = glm::normalize(fingerTip - fingerRoot); + break; + } } - glm::vec3 tip = palm.getPosition(); - glm::vec3 root = palm.getPosition() + palm.getNormal() * palmThickness; - const float radiusA = 0.05f; - const float radiusB = 0.03f; - Avatar::renderJointConnectingCone(root, tip, radiusA, radiusB); + // compute paddle position + glm::vec3 handPosition; + if (i == SIXENSE_CONTROLLER_ID_LEFT_HAND) { + _owningAvatar->getSkeletonModel().getLeftHandPosition(handPosition); + } else if (i == SIXENSE_CONTROLLER_ID_RIGHT_HAND) { + _owningAvatar->getSkeletonModel().getRightHandPosition(handPosition); + } + glm::vec3 tip = handPosition + HAND_PADDLE_OFFSET * fingerAxis; + glm::vec3 root = tip + palm->getNormal() * HAND_PADDLE_THICKNESS; + // render a very shallow cone as the paddle + Avatar::renderJointConnectingCone(root, tip, HAND_PADDLE_RADIUS, 0.f); } } + glDepthMask(GL_TRUE); glEnable(GL_DEPTH_TEST); diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 8a672fc1d9..bc671d1926 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -30,6 +30,10 @@ class Avatar; class ProgramObject; +const float HAND_PADDLE_OFFSET = 0.1f; +const float HAND_PADDLE_THICKNESS = 0.05f; +const float HAND_PADDLE_RADIUS = 0.15f; + class Hand : public HandData { public: Hand(Avatar* owningAvatar); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 50b6c4f072..ae16bd81f8 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -8,6 +8,8 @@ #include +#include + #include "Application.h" #include "Avatar.h" #include "Menu.h" diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 776eb29e3b..e5ca5c7623 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -9,7 +9,6 @@ #ifndef __interface__SkeletonModel__ #define __interface__SkeletonModel__ -#include #include "renderer/Model.h" diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index b45b7f7364..65c0d9332f 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -67,6 +67,9 @@ void OculusManager::connect() { _scaleLocation = _program.uniformLocation("scale"); _scaleInLocation = _program.uniformLocation("scaleIn"); _hmdWarpParamLocation = _program.uniformLocation("hmdWarpParam"); + } else { + _deviceManager.Clear(); + System::Destroy(); } #endif } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 570d778c96..b5de82ebb6 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -488,6 +488,22 @@ bool Model::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePos return getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) && getJointPosition(geometry.rightEyeJointIndex, secondEyePosition); } + +bool Model::getLeftHandPosition(glm::vec3& position) const { + return getJointPosition(getLeftHandJointIndex(), position); +} + +bool Model::getLeftHandRotation(glm::quat& rotation) const { + return getJointRotation(getLeftHandJointIndex(), rotation); +} + +bool Model::getRightHandPosition(glm::vec3& position) const { + return getJointPosition(getRightHandJointIndex(), position); +} + +bool Model::getRightHandRotation(glm::quat& rotation) const { + return getJointRotation(getRightHandJointIndex(), rotation); +} bool Model::setLeftHandPosition(const glm::vec3& position) { return setJointPosition(getLeftHandJointIndex(), position); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index fc3a0687b8..ae4303caea 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -82,6 +82,22 @@ public: /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; + /// Retrieve the position of the left hand + /// \return true whether or not the position was found + bool getLeftHandPosition(glm::vec3& position) const; + + /// Retrieve the rotation of the left hand + /// \return true whether or not the rotation was found + bool getLeftHandRotation(glm::quat& rotation) const; + + /// Retrieve the position of the right hand + /// \return true whether or not the position was found + bool getRightHandPosition(glm::vec3& position) const; + + /// Retrieve the rotation of the right hand + /// \return true whether or not the rotation was found + bool getRightHandRotation(glm::quat& rotation) const; + /// Sets the position of the left hand using inverse kinematics. /// \return whether or not the left hand joint was found bool setLeftHandPosition(const glm::vec3& position); diff --git a/interface/src/starfield/Controller.cpp b/interface/src/starfield/Controller.cpp index b738749416..0987a14dd5 100755 --- a/interface/src/starfield/Controller.cpp +++ b/interface/src/starfield/Controller.cpp @@ -24,7 +24,8 @@ bool Controller::computeStars(unsigned numStars, unsigned seed) { this->retile(numStars, _tileResolution); - qDebug() << "Total time to generate stars: " << ((usecTimestampNow() - usecTimestamp(&startTime)) / 1000) << "msec"; + qDebug() << "Total time to retile and generate stars: " + << ((usecTimestampNow() - usecTimestamp(&startTime)) / 1000) << "msec"; return true; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 83bcb41eea..923d5510b0 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -33,9 +33,10 @@ typedef unsigned long long uint64_t; #include #include +#include #include - #include + #include "HeadData.h" #include "HandData.h" @@ -131,6 +132,10 @@ public: virtual bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, int skeletonSkipIndex = -1) const { return false; } + virtual bool findSphereCollision(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) { + return false; + } + protected: QUuid _uuid; diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index 44f291411c..b8704777de 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -38,8 +38,17 @@ PalmData& HandData::addNewPalm() { return _palms.back(); } -const int SIXENSE_CONTROLLER_ID_LEFT_HAND = 0; -const int SIXENSE_CONTROLLER_ID_RIGHT_HAND = 1; +const PalmData* HandData::getPalm(int sixSenseID) const { + // the palms are not necessarily added in left-right order, + // so we have to search for the right SixSenseID + for (int i = 0; i < _palms.size(); i++) { + const PalmData* palm = &(_palms[i]); + if (palm->getSixenseID() == sixSenseID) { + return palm->isActive() ? palm : NULL; + } + } + return NULL; +} void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const { leftPalmIndex = -1; diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index a0c8fed980..1aa60da927 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -35,6 +35,9 @@ const int BUTTON_FWD = 128; const float LEAP_UNIT_SCALE = 0.001f; ///< convert mm to meters +const int SIXENSE_CONTROLLER_ID_LEFT_HAND = 0; +const int SIXENSE_CONTROLLER_ID_RIGHT_HAND = 1; + class HandData { public: HandData(AvatarData* owningAvatar); @@ -55,6 +58,7 @@ public: std::vector& getPalms() { return _palms; } const std::vector& getPalms() const { return _palms; } + const PalmData* getPalm(int sixSenseID) const; size_t getNumPalms() const { return _palms.size(); } PalmData& addNewPalm(); diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index f478fe9cf1..a57475b4db 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -137,15 +138,14 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA) void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { - // particles that are in hand, don't collide with other avatar parts + // particles that are in hand, don't collide with avatars if (particle->getInHand()) { return; } - //printf("updateCollisionWithAvatars()...\n"); glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE); float radius = particle->getRadius() * (float)(TREE_SCALE); - const float ELASTICITY = 0.4f; + const float ELASTICITY = 0.95f; const float DAMPING = 0.0f; const float COLLISION_FREQUENCY = 0.5f; glm::vec3 penetration; @@ -154,77 +154,40 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { // first check the selfAvatar if set... if (_selfAvatar) { AvatarData* avatar = (AvatarData*)_selfAvatar; - //printf("updateCollisionWithAvatars()..._selfAvatar=%p\n", avatar); - - // check hands... - const HandData* handData = avatar->getHandData(); - - // TODO: combine hand and collision check into one. Note: would need to supply - // CollisionInfo class rather than just vec3 (penetration) so we can get back - // added velocity. - - if (handData->findSpherePenetration(center, radius, penetration, collidingPalm)) { - // TODO: dot collidingPalm and hand velocities and skip collision when they are moving apart. - // apply a hard collision when ball collides with hand - penetration /= (float)(TREE_SCALE); - updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - - // determine if the palm that collided was moving, if so, then we add that palm velocity as well... - glm::vec3 addedVelocity = NO_ADDED_VELOCITY; - if (collidingPalm) { - glm::vec3 palmVelocity = collidingPalm->getVelocity() / (float)(TREE_SCALE); - //printf("collidingPalm Velocity=%f,%f,%f\n", palmVelocity.x, palmVelocity.y, palmVelocity.z); - addedVelocity = palmVelocity; + CollisionInfo collision; + if (avatar->findSphereCollision(center, radius, collision)) { + if (glm::dot(particle->getVelocity(), collision._addedVelocity) < 0.f) { + // only collide when particle and collision point are moving toward each other + collision._penetration /= (float)(TREE_SCALE); + collision._addedVelocity /= (float)(TREE_SCALE); + updateCollisionSound(particle, collision._penetration, COLLISION_FREQUENCY); + applyHardCollision(particle, collision._penetration, ELASTICITY, DAMPING, collision._addedVelocity); } - - applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); - } else if (avatar->findSpherePenetration(center, radius, penetration)) { - // apply hard collision when particle collides with avatar - penetration /= (float)(TREE_SCALE); - updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - glm::vec3 addedVelocity = avatar->getVelocity(); - applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); } } // loop through all the other avatars for potential interactions... - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { //qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n"; if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { - // TODO: dot collidingPalm and hand velocities and skip collision when they are moving apart. AvatarData* avatar = static_cast(node->getLinkedData()); - //printf("updateCollisionWithAvatars()...avatar=%p\n", avatar); - - // check hands... - const HandData* handData = avatar->getHandData(); - - if (handData->findSpherePenetration(center, radius, penetration, collidingPalm)) { - // apply a hard collision when ball collides with hand - penetration /= (float)(TREE_SCALE); - updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - - // determine if the palm that collided was moving, if so, then we add that palm velocity as well... - glm::vec3 addedVelocity = NO_ADDED_VELOCITY; - if (collidingPalm) { - glm::vec3 palmVelocity = collidingPalm->getVelocity() / (float)(TREE_SCALE); - //printf("collidingPalm Velocity=%f,%f,%f\n", palmVelocity.x, palmVelocity.y, palmVelocity.z); - addedVelocity = palmVelocity; + + CollisionInfo collision; + if (avatar->findSphereCollision(center, radius, collision)) { + if (glm::dot(particle->getVelocity(), collision._addedVelocity) < 0.f) { + // only collide when particle and collision point are moving toward each other + collision._penetration /= (float)(TREE_SCALE); + collision._addedVelocity /= (float)(TREE_SCALE); + updateCollisionSound(particle, collision._penetration, COLLISION_FREQUENCY); + applyHardCollision(particle, collision._penetration, ELASTICITY, DAMPING, collision._addedVelocity); } - - applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); - - } else if (avatar->findSpherePenetration(center, radius, penetration)) { - penetration /= (float)(TREE_SCALE); - updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); - glm::vec3 addedVelocity = avatar->getVelocity(); - applyHardCollision(particle, penetration, ELASTICITY, DAMPING, addedVelocity); } } } } +// TODO: convert applyHardCollision() to take a CollisionInfo& instead of penetration + addedVelocity void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::vec3& penetration, float elasticity, float damping, const glm::vec3& addedVelocity) { // diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index 920b89c053..13766a0264 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -61,4 +61,4 @@ private: AvatarData* _selfAvatar; }; -#endif /* defined(__hifi__ParticleCollisionSystem__) */ \ No newline at end of file +#endif /* defined(__hifi__ParticleCollisionSystem__) */ diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h new file mode 100644 index 0000000000..4f46a6bfd3 --- /dev/null +++ b/libraries/shared/src/CollisionInfo.h @@ -0,0 +1,26 @@ +// +// CollisionInfo.h +// hifi +// +// Created by Andrew Meadows on 2014.01.13 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__CollisionInfo__ +#define __hifi__CollisionInfo__ + +#include + +class CollisionInfo { +public: + CollisionInfo() : _penetration(0.f), _addedVelocity(0.f) { } + ~CollisionInfo() {} + + //glm::vec3 _point; + //glm::vec3 _normal; + glm::vec3 _penetration; + glm::vec3 _addedVelocity; +}; + + +#endif /* defined(__hifi__CollisionInfo__) */ diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 5d6e216ad5..2627a9035a 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -114,6 +114,30 @@ bool findSpherePlanePenetration(const glm::vec3& sphereCenter, float sphereRadiu return false; } +bool findSphereDiskPenetration(const glm::vec3& sphereCenter, float sphereRadius, + const glm::vec3& diskCenter, float diskRadius, const glm::vec3& diskNormal, + glm::vec3& penetration) { + glm::vec3 localCenter = sphereCenter - diskCenter; + float verticalDistance = glm::dot(localCenter, diskNormal); + + + if (abs(verticalDistance) < sphereRadius) { + // sphere hit the plane, but does it hit the disk? + // Note: this algorithm ignores edge hits. + glm::vec3 verticalOffset = verticalDistance * diskNormal; + if (glm::length(localCenter - verticalOffset) < diskRadius) { + // yes, hit the disk + penetration = (sphereRadius - abs(verticalDistance)) * diskNormal; + if (verticalDistance < 0.f) { + // hit the backside of the disk, so negate penetration vector + penetration *= -1.f; + } + return true; + } + } + return false; +} + bool findCapsuleSpherePenetration(const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius, const glm::vec3& sphereCenter, float sphereRadius, glm::vec3& penetration) { if (findSphereCapsulePenetration(sphereCenter, sphereRadius, diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 2b782b43ae..59ad055445 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -47,6 +47,17 @@ bool findSphereCapsuleConePenetration(const glm::vec3& sphereCenter, float spher bool findSpherePlanePenetration(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec4& plane, glm::vec3& penetration); +/// Computes the penetration between a sphere and a disk. +/// \param sphereCenter center of sphere +/// \param sphereRadius radius of sphere +/// \param diskCenter center of disk +/// \param diskRadius radius of disk +/// \param diskNormal normal of disk plan +/// \return true if sphere touches disk (does not handle collisions with disk edge) +bool findSphereDiskPenetration(const glm::vec3& sphereCenter, float sphereRadius, + const glm::vec3& diskCenter, float diskRadius, const glm::vec3& diskNormal, + glm::vec3& penetration); + bool findCapsuleSpherePenetration(const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius, const glm::vec3& sphereCenter, float sphereRadius, glm::vec3& penetration); diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 021234b8ee..391e329200 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -99,7 +99,7 @@ public: int fillPingPacket(unsigned char* buffer); int fillPingReplyPacket(unsigned char* pingBuffer, unsigned char* replyBuffer); void pingPublicAndLocalSocketsForInactiveNode(Node* node); - void killNodeWithUUID(const QUuid& nodeUUID); + void sendKillNode(const char* nodeTypes, int numNodeTypes); SharedNodePointer nodeWithAddress(const HifiSockAddr& senderSockAddr); @@ -123,6 +123,8 @@ public slots: void sendDomainServerCheckIn(); void pingInactiveNodes(); void removeSilentNodes(); + + void killNodeWithUUID(const QUuid& nodeUUID); signals: void domainChanged(const QString& domainHostname); void nodeAdded(SharedNodePointer);