diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index dbf2907cc0..b0d3d06994 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -611,22 +611,14 @@ bool DomainServer::isPacketVerified(const udt::Packet& packet) { // let the NodeList do its checks now (but pass it the sourceNode so it doesn't need to look it up again) return nodeList->isPacketVerifiedWithSource(packet, sourceNode.data()); } else { - static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unmatched IP for UUID"; - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX); - - qDebug() << "Packet of type" << headerType - << "received from unmatched IP for UUID" << uuidStringWithoutCurlyBraces(sourceID); + HIFI_FDEBUG("Packet of type" << headerType + << "received from unmatched IP for UUID" << uuidStringWithoutCurlyBraces(sourceID)); return false; } } else { - static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown node with UUID"; - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX); - - qDebug() << "Packet of type" << headerType - << "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID); + HIFI_FDEBUG("Packet of type" << headerType + << "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID)); return false; } @@ -1242,31 +1234,16 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointer NOISY_MESSAGE_INTERVAL_MSECS) { - static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex - ("Received a request for assignment type [^ ]+ from [^ ]+"); + static bool printedAssignmentTypeMessage = false; + if (!printedAssignmentTypeMessage && requestAssignment.getType() != Assignment::AgentType) { + printedAssignmentTypeMessage = true; qDebug() << "Received a request for assignment type" << requestAssignment.getType() << "from" << message->getSenderSockAddr(); - noisyMessageTimer.restart(); } SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); @@ -1300,13 +1277,11 @@ void DomainServer::processRequestAssignmentPacket(QSharedPointergetUUID(), requestAssignment.getWalletUUID(), requestAssignment.getNodeVersion()); } else { - if (requestAssignment.getType() != Assignment::AgentType - || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { - static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex - ("Unable to fulfill assignment request of type [^ ]+ from [^ ]+"); + static bool printedAssignmentRequestMessage = false; + if (!printedAssignmentRequestMessage && requestAssignment.getType() != Assignment::AgentType) { + printedAssignmentRequestMessage = true; qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() << "from" << message->getSenderSockAddr(); - noisyMessageTimer.restart(); } } } @@ -1552,10 +1527,12 @@ void DomainServer::sendICEServerAddressToMetaverseAPI() { callbackParameters.jsonCallbackReceiver = this; callbackParameters.jsonCallbackMethod = "handleSuccessfulICEServerAddressUpdate"; - static QString repeatedMessage = LogHandler::getInstance().addOnlyOnceMessageRegex - ("Updating ice-server address in High Fidelity Metaverse API to [^ \n]+"); - qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" - << (_iceServerSocket.isNull() ? "" : _iceServerSocket.getAddress().toString()); + static bool printedIceServerMessage = false; + if (!printedIceServerMessage) { + printedIceServerMessage = true; + qDebug() << "Updating ice-server address in High Fidelity Metaverse API to" + << (_iceServerSocket.isNull() ? "" : _iceServerSocket.getAddress().toString()); + } static const QString DOMAIN_ICE_ADDRESS_UPDATE = "/api/v1/domains/%1/ice_server_address"; diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index fe00d86c3a..ac9441319b 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -180,8 +180,9 @@ else () add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) endif () + if (BUILD_TOOLS AND NPM_EXECUTABLE) - # require JSDoc to be build before interface is deployed (Console Auto-complete) + # require JSDoc to be build before interface is deployed add_dependencies(resources jsdoc) endif() @@ -322,6 +323,13 @@ if (APPLE) "${RESOURCES_DEV_DIR}/scripts" ) + # copy JSDoc files beside the executable + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" + "${RESOURCES_DEV_DIR}/jsdoc" + ) + # call the fixup_interface macro to add required bundling commands for installation fixup_interface() @@ -350,6 +358,13 @@ else() "${RESOURCES_DEV_DIR}/serverless/tutorial.json" ) + # copy JSDoc files beside the executable + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/tools/jsdoc/out" + "${INTERFACE_EXEC_DIR}/jsdoc" + ) + # link target to external libraries if (WIN32) target_link_libraries(${TARGET_NAME} wsock32.lib Winmm.lib) diff --git a/interface/resources/sounds/snap.wav b/interface/resources/sounds/snapshot/snap.wav similarity index 100% rename from interface/resources/sounds/snap.wav rename to interface/resources/sounds/snapshot/snap.wav diff --git a/scripts/system/assets/sounds/sound-print-photo.wav b/interface/resources/sounds/snapshot/sound-print-photo.wav similarity index 100% rename from scripts/system/assets/sounds/sound-print-photo.wav rename to interface/resources/sounds/snapshot/sound-print-photo.wav diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 477df0bdd0..83d6bc1b28 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1266,9 +1266,32 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Make sure the window is set to the correct size by processing the pending events QCoreApplication::processEvents(); _glWidget->createContext(); - _glWidget->makeCurrent(); + // Create the main thread context, the GPU backend, and the display plugins initializeGL(); + qCDebug(interfaceapp, "Initialized Display."); + // Create the rendering engine. This can be slow on some machines due to lots of + // GPU pipeline creation. + initializeRenderEngine(); + qCDebug(interfaceapp, "Initialized Render Engine."); + + // Initialize the user interface and menu system + // Needs to happen AFTER the render engine initialization to access its configuration + initializeUi(); + + init(); + qCDebug(interfaceapp, "init() complete."); + + // create thread for parsing of octree data independent of the main network and rendering threads + _octreeProcessor.initialize(_enableProcessOctreeThread); + connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); + _entityEditSender.initialize(_enableProcessOctreeThread); + + _idleLoopStdev.reset(); + + // update before the first render + update(0); + // Make sure we don't time out during slow operations at startup updateHeartbeat(); @@ -2430,48 +2453,47 @@ void Application::initializeGL() { _isGLInitialized = true; } - // Build a shared canvas / context for the Chromium processes _glWidget->makeCurrent(); + glClearColor(0.2f, 0.2f, 0.2f, 1); + glClear(GL_COLOR_BUFFER_BIT); + _glWidget->swapBuffers(); -#if !defined(DISABLE_QML) - // Chromium rendering uses some GL functions that prevent nSight from capturing - // frames, so we only create the shared context if nsight is NOT active. - if (!nsightActive()) { - _chromiumShareContext = new OffscreenGLCanvas(); - _chromiumShareContext->setObjectName("ChromiumShareContext"); - _chromiumShareContext->create(_glWidget->qglContext()); - _chromiumShareContext->makeCurrent(); - if (!_chromiumShareContext->makeCurrent()) { - qCWarning(interfaceapp, "Unable to make chromium shared context current"); - } - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); - } else { - qCWarning(interfaceapp) << "nSIGHT detected, disabling chrome rendering"; + // Build an offscreen GL context for the main thread. + _offscreenContext = new OffscreenGLCanvas(); + _offscreenContext->setObjectName("MainThreadContext"); + _offscreenContext->create(_glWidget->qglContext()); + if (!_offscreenContext->makeCurrent()) { + qFatal("Unable to make offscreen context current"); } -#endif + _offscreenContext->doneCurrent(); + _offscreenContext->setThreadContext(); - // Build a shared canvas / context for the QML rendering - _glWidget->makeCurrent(); - _qmlShareContext = new OffscreenGLCanvas(); - _qmlShareContext->setObjectName("QmlShareContext"); - _qmlShareContext->create(_glWidget->qglContext()); - if (!_qmlShareContext->makeCurrent()) { - qCWarning(interfaceapp, "Unable to make QML shared context current"); + // Move the GL widget context to the render event handler thread + _renderEventHandler = new RenderEventHandler(_glWidget->qglContext()); + if (!_offscreenContext->makeCurrent()) { + qFatal("Unable to make offscreen context current"); } - OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext()); - _qmlShareContext->doneCurrent(); + // Create the GPU backend + + // Requires the window context, because that's what's used in the actual rendering + // and the GPU backend will make things like the VAO which cannot be shared across + // contexts _glWidget->makeCurrent(); gpu::Context::init(); qApp->setProperty(hifi::properties::gl::MAKE_PROGRAM_CALLBACK, QVariant::fromValue((void*)(&gpu::gl::GLBackend::makeProgram))); - _gpuContext = std::make_shared(); - // The gpu context can make child contexts for transfers, so - // we need to restore primary rendering context _glWidget->makeCurrent(); + _gpuContext = std::make_shared(); - initDisplay(); - qCDebug(interfaceapp, "Initialized Display."); + // Restore the default main thread context + _offscreenContext->makeCurrent(); + + updateDisplayMode(); +} + +void Application::initializeRenderEngine() { + _offscreenContext->makeCurrent(); // FIXME: on low end systems os the shaders take up to 1 minute to compile, so we pause the deadlock watchdog thread. DeadlockWatchdogThread::withPause([&] { @@ -2488,66 +2510,44 @@ void Application::initializeGL() { // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. DependencyManager::get()->initializeShapePipelines(); }); - - _offscreenContext = new OffscreenGLCanvas(); - _offscreenContext->setObjectName("MainThreadContext"); - _offscreenContext->create(_glWidget->qglContext()); - if (!_offscreenContext->makeCurrent()) { - qFatal("Unable to make offscreen context current"); - } - _offscreenContext->doneCurrent(); - _offscreenContext->setThreadContext(); - _renderEventHandler = new RenderEventHandler(_glWidget->qglContext()); - - // The UI can't be created until the primary OpenGL - // context is created, because it needs to share - // texture resources - // Needs to happen AFTER the render engine initialization to access its configuration - if (!_offscreenContext->makeCurrent()) { - qFatal("Unable to make offscreen context current"); - } - - initializeUi(); - qCDebug(interfaceapp, "Initialized Offscreen UI."); - - if (!_offscreenContext->makeCurrent()) { - qFatal("Unable to make offscreen context current"); - } - init(); - qCDebug(interfaceapp, "init() complete."); - - // create thread for parsing of octree data independent of the main network and rendering threads - _octreeProcessor.initialize(_enableProcessOctreeThread); - connect(&_octreeProcessor, &OctreePacketProcessor::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch); - _entityEditSender.initialize(_enableProcessOctreeThread); - - _idleLoopStdev.reset(); - - - // Restore the primary GL content for the main thread - if (!_offscreenContext->makeCurrent()) { - qFatal("Unable to make offscreen context current"); - } - - // update before the first render - update(0); } extern void setupPreferences(); void Application::initializeUi() { + // Build a shared canvas / context for the Chromium processes +#if !defined(DISABLE_QML) + // Chromium rendering uses some GL functions that prevent nSight from capturing + // frames, so we only create the shared context if nsight is NOT active. + if (!nsightActive()) { + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); + _chromiumShareContext->create(_offscreenContext->getContext()); + if (!_chromiumShareContext->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make chromium shared context current"); + } + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + _chromiumShareContext->doneCurrent(); + // Restore the GL widget context + _offscreenContext->makeCurrent(); + } else { + qCWarning(interfaceapp) << "nSIGHT detected, disabling chrome rendering"; + } +#endif + + // Build a shared canvas / context for the QML rendering + _qmlShareContext = new OffscreenGLCanvas(); + _qmlShareContext->setObjectName("QmlShareContext"); + _qmlShareContext->create(_offscreenContext->getContext()); + if (!_qmlShareContext->makeCurrent()) { + qCWarning(interfaceapp, "Unable to make QML shared context current"); + } + OffscreenQmlSurface::setSharedContext(_qmlShareContext->getContext()); + _qmlShareContext->doneCurrent(); + // Restore the GL widget context + _offscreenContext->makeCurrent(); // Make sure all QML surfaces share the main thread GL context OffscreenQmlSurface::setSharedContext(_offscreenContext->getContext()); - OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "OverlayWindowTest.qml" }, - [](QQmlContext* context) { - qDebug() << "Whitelist OverlayWindow worked"; - context->setContextProperty("OverlayWindowTestString", "TestWorked"); - }); - OffscreenQmlSurface::addWhitelistContextHandler(QUrl{ "hifi/audio/Audio.qml" }, - [](QQmlContext* context) { - qDebug() << "QQQ" << __FUNCTION__ << "Whitelist Audio worked"; - }); - AddressBarDialog::registerType(); ErrorDialog::registerType(); @@ -2650,6 +2650,10 @@ void Application::initializeUi() { auto offscreenSurfaceCache = DependencyManager::get(); offscreenSurfaceCache->reserve(TabletScriptingInterface::QML, 1); offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); + + // Now that the menu is instantiated, ensure the display plugin menu is properly updated + updateDisplayMode(); + flushMenuUpdates(); } @@ -4611,11 +4615,8 @@ QVector Application::pasteEntities(float x, float y, float z) { return _entityClipboard->sendEntities(&_entityEditSender, getEntities()->getTree(), x, y, z); } -void Application::initDisplay() { -} - void Application::init() { - + _offscreenContext->makeCurrent(); // Make sure Login state is up to date DependencyManager::get()->toggleLoginDialog(); if (!DISABLE_DEFERRED) { @@ -4821,7 +4822,7 @@ void Application::updateThreads(float deltaTime) { void Application::toggleOverlays() { auto menu = Menu::getInstance(); - menu->setIsOptionChecked(MenuOption::Overlays, menu->isOptionChecked(MenuOption::Overlays)); + menu->setIsOptionChecked(MenuOption::Overlays, !menu->isOptionChecked(MenuOption::Overlays)); } void Application::setOverlaysVisible(bool visible) { @@ -5260,12 +5261,12 @@ void Application::update(float deltaTime) { QSharedPointer avatarManager = DependencyManager::get(); { - PROFILE_RANGE(simulation_physics, "Physics"); - PerformanceTimer perfTimer("physics"); + PROFILE_RANGE(simulation_physics, "Simulation"); + PerformanceTimer perfTimer("simulation"); if (_physicsEnabled) { { - PROFILE_RANGE(simulation_physics, "PreStep"); - PerformanceTimer perfTimer("preStep)"); + PROFILE_RANGE(simulation_physics, "PrePhysics"); + PerformanceTimer perfTimer("prePhysics)"); { const VectorOfMotionStates& motionStates = _entitySimulation->getObjectsToRemoveFromPhysics(); _physicsEngine->removeObjects(motionStates); @@ -5299,59 +5300,63 @@ void Application::update(float deltaTime) { }); } { - PROFILE_RANGE(simulation_physics, "Step"); - PerformanceTimer perfTimer("step"); + PROFILE_RANGE(simulation_physics, "StepPhysics"); + PerformanceTimer perfTimer("stepPhysics"); getEntities()->getTree()->withWriteLock([&] { _physicsEngine->stepSimulation(); }); } { - PROFILE_RANGE(simulation_physics, "PostStep"); - PerformanceTimer perfTimer("postStep"); if (_physicsEngine->hasOutgoingChanges()) { - // grab the collision events BEFORE handleOutgoingChanges() because at this point - // we have a better idea of which objects we own or should own. - auto& collisionEvents = _physicsEngine->getCollisionEvents(); + { + PROFILE_RANGE(simulation_physics, "PostPhysics"); + PerformanceTimer perfTimer("postPhysics"); + // grab the collision events BEFORE handleChangedMotionStates() because at this point + // we have a better idea of which objects we own or should own. + auto& collisionEvents = _physicsEngine->getCollisionEvents(); - getEntities()->getTree()->withWriteLock([&] { - PROFILE_RANGE(simulation_physics, "HandleChanges"); - PerformanceTimer perfTimer("handleChanges"); + getEntities()->getTree()->withWriteLock([&] { + PROFILE_RANGE(simulation_physics, "HandleChanges"); + PerformanceTimer perfTimer("handleChanges"); - const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); - _entitySimulation->handleChangedMotionStates(outgoingChanges); - avatarManager->handleChangedMotionStates(outgoingChanges); + const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates(); + _entitySimulation->handleChangedMotionStates(outgoingChanges); + avatarManager->handleChangedMotionStates(outgoingChanges); - const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates(); - _entitySimulation->handleDeactivatedMotionStates(deactivations); - }); + const VectorOfMotionStates& deactivations = _physicsEngine->getDeactivatedMotionStates(); + _entitySimulation->handleDeactivatedMotionStates(deactivations); + }); - if (!_aboutToQuit) { - // handleCollisionEvents() AFTER handleOutgoingChanges() - { - PROFILE_RANGE(simulation_physics, "CollisionEvents"); - avatarManager->handleCollisionEvents(collisionEvents); - // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk - // deadlock.) - _entitySimulation->handleCollisionEvents(collisionEvents); + if (!_aboutToQuit) { + // handleCollisionEvents() AFTER handleChangedMotionStates() + { + PROFILE_RANGE(simulation_physics, "CollisionEvents"); + avatarManager->handleCollisionEvents(collisionEvents); + // Collision events (and their scripts) must not be handled when we're locked, above. (That would risk + // deadlock.) + _entitySimulation->handleCollisionEvents(collisionEvents); + } } + { + PROFILE_RANGE(simulation_physics, "MyAvatar"); + myAvatar->harvestResultsFromPhysicsSimulation(deltaTime); + } + + if (PerformanceTimer::isActive() && + Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && + Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsTiming)) { + _physicsEngine->harvestPerformanceStats(); + } + // NOTE: the PhysicsEngine stats are written to stdout NOT to Qt log framework + _physicsEngine->dumpStatsIfNecessary(); + } + + if (!_aboutToQuit) { // NOTE: the getEntities()->update() call below will wait for lock - // and will simulate entity motion (the EntityTree has been given an EntitySimulation). + // and will provide non-physical entity motion getEntities()->update(true); // update the models... } - - { - PROFILE_RANGE(simulation_physics, "MyAvatar"); - myAvatar->harvestResultsFromPhysicsSimulation(deltaTime); - } - - if (PerformanceTimer::isActive() && - Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) && - Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) { - _physicsEngine->harvestPerformanceStats(); - } - // NOTE: the PhysicsEngine stats are written to stdout NOT to Qt log framework - _physicsEngine->dumpStatsIfNecessary(); } } } else { @@ -6015,9 +6020,7 @@ bool Application::nearbyEntitiesAreReadyForPhysics() { bool result = true; foreach (EntityItemPointer entity, entities) { if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) { - static QString repeatedMessage = - LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*"); - qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName(); + HIFI_FCDEBUG(interfaceapp(), "Physics disabled until entity loads: " << entity->getID() << entity->getName()); // don't break here because we want all the relevant entities to start their downloads result = false; } @@ -7507,21 +7510,34 @@ void Application::updateDisplayMode() { qFatal("Attempted to switch display plugins from a non-main thread"); } - auto menu = Menu::getInstance(); auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); + // Once time initialization code static std::once_flag once; std::call_once(once, [&] { - bool first = true; - - // first sort the plugins into groupings: standard, advanced, developer - DisplayPluginList standard; - DisplayPluginList advanced; - DisplayPluginList developer; foreach(auto displayPlugin, displayPlugins) { displayPlugin->setContext(_gpuContext); - auto grouping = displayPlugin->getGrouping(); - switch (grouping) { + QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, + [this](const QSize& size) { resizeGL(); }); + QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset); + } + }); + + // Once time initialization code that depends on the UI being available + auto menu = Menu::getInstance(); + if (menu) { + static std::once_flag onceUi; + std::call_once(onceUi, [&] { + bool first = true; + + // first sort the plugins into groupings: standard, advanced, developer + DisplayPluginList standard; + DisplayPluginList advanced; + DisplayPluginList developer; + foreach(auto displayPlugin, displayPlugins) { + displayPlugin->setContext(_gpuContext); + auto grouping = displayPlugin->getGrouping(); + switch (grouping) { case Plugin::ADVANCED: advanced.push_back(displayPlugin); break; @@ -7531,42 +7547,40 @@ void Application::updateDisplayMode() { default: standard.push_back(displayPlugin); break; + } } - } - // concatenate the groupings into a single list in the order: standard, advanced, developer - standard.insert(std::end(standard), std::begin(advanced), std::end(advanced)); - standard.insert(std::end(standard), std::begin(developer), std::end(developer)); + // concatenate the groupings into a single list in the order: standard, advanced, developer + standard.insert(std::end(standard), std::begin(advanced), std::end(advanced)); + standard.insert(std::end(standard), std::begin(developer), std::end(developer)); - foreach(auto displayPlugin, standard) { - addDisplayPluginToMenu(displayPlugin, first); - auto displayPluginName = displayPlugin->getName(); - QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) { - resizeGL(); - }); - QObject::connect(displayPlugin.get(), &DisplayPlugin::resetSensorsRequested, this, &Application::requestReset); - first = false; - } + foreach(auto displayPlugin, standard) { + addDisplayPluginToMenu(displayPlugin, first); + first = false; + } - // after all plugins have been added to the menu, add a separator to the menu - auto menu = Menu::getInstance(); - auto parent = menu->getMenu(MenuOption::OutputMenu); - parent->addSeparator(); - }); + // after all plugins have been added to the menu, add a separator to the menu + auto parent = menu->getMenu(MenuOption::OutputMenu); + parent->addSeparator(); + }); + + } // Default to the first item on the list, in case none of the menu items match DisplayPluginPointer newDisplayPlugin = displayPlugins.at(0); - foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { - QString name = displayPlugin->getName(); - QAction* action = menu->getActionForOption(name); - // Menu might have been removed if the display plugin lost - if (!action) { - continue; - } - if (action->isChecked()) { - newDisplayPlugin = displayPlugin; - break; + if (menu) { + foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { + QString name = displayPlugin->getName(); + QAction* action = menu->getActionForOption(name); + // Menu might have been removed if the display plugin lost + if (!action) { + continue; + } + if (action->isChecked()) { + newDisplayPlugin = displayPlugin; + break; + } } } @@ -7574,8 +7588,13 @@ void Application::updateDisplayMode() { return; } + setDisplayPlugin(newDisplayPlugin); +} + +void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { auto offscreenUi = DependencyManager::get(); auto desktop = offscreenUi->getDesktop(); + auto menu = Menu::getInstance(); // Make the switch atomic from the perspective of other threads { @@ -7596,6 +7615,8 @@ void Application::updateDisplayMode() { bool active = newDisplayPlugin->activate(); if (!active) { + auto displayPlugins = PluginManager::getInstance()->getDisplayPlugins(); + // If the new plugin fails to activate, fallback to last display qWarning() << "Failed to activate display: " << newDisplayPlugin->getName(); newDisplayPlugin = oldDisplayPlugin; @@ -7616,13 +7637,6 @@ void Application::updateDisplayMode() { if (!active) { qFatal("Failed to activate fallback plugin"); } - - // We've changed the selection - it should be reflected in the menu - QAction* action = menu->getActionForOption(newDisplayPlugin->getName()); - if (!action) { - qFatal("Failed to find activated plugin"); - } - action->setChecked(true); } offscreenUi->resize(fromGlm(newDisplayPlugin->getRecommendedUiSize())); @@ -7649,14 +7663,21 @@ void Application::updateDisplayMode() { getMyAvatar()->reset(false); // switch to first person if entering hmd and setting is checked - if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) { - menu->setIsOptionChecked(MenuOption::FirstPerson, true); - cameraMenuChanged(); - } + if (menu) { + QAction* action = menu->getActionForOption(newDisplayPlugin->getName()); + if (action) { + action->setChecked(true); + } - // Remove the mirror camera option from menu if in HMD mode - auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror); - mirrorAction->setVisible(!isHmd); + if (isHmd && menu->isOptionChecked(MenuOption::FirstPersonHMD)) { + menu->setIsOptionChecked(MenuOption::FirstPerson, true); + cameraMenuChanged(); + } + + // Remove the mirror camera option from menu if in HMD mode + auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror); + mirrorAction->setVisible(!isHmd); + } Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin"); } @@ -7732,15 +7753,18 @@ void Application::unresponsiveApplication() { } void Application::setActiveDisplayPlugin(const QString& pluginName) { - auto menu = Menu::getInstance(); - foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) { + DisplayPluginPointer newDisplayPlugin; + for (DisplayPluginPointer displayPlugin : PluginManager::getInstance()->getDisplayPlugins()) { QString name = displayPlugin->getName(); - QAction* action = menu->getActionForOption(name); if (pluginName == name) { - action->setChecked(true); + newDisplayPlugin = displayPlugin; + break; } } - updateDisplayMode(); + + if (newDisplayPlugin) { + setDisplayPlugin(newDisplayPlugin); + } } void Application::handleLocalServerConnection() const { diff --git a/interface/src/Application.h b/interface/src/Application.h index d7fbb48a58..769658b0d6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -145,6 +145,7 @@ public: Q_INVOKABLE QString getUserAgent(); void initializeGL(); + void initializeRenderEngine(); void initializeUi(); void updateCamera(RenderArgs& renderArgs, float deltaTime); @@ -437,6 +438,7 @@ private slots: static void packetSent(quint64 length); static void addingEntityWithCertificate(const QString& certificateID, const QString& placeName); void updateDisplayMode(); + void setDisplayPlugin(DisplayPluginPointer newPlugin); void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo); void addAssetToWorldCheckModelSize(); @@ -449,7 +451,6 @@ private slots: void switchDisplayMode(); private: - static void initDisplay(); void init(); bool handleKeyEventForFocusedEntityOrOverlay(QEvent* event); bool handleFileOpenEvent(QFileOpenEvent* event); diff --git a/interface/src/InterfaceDynamicFactory.cpp b/interface/src/InterfaceDynamicFactory.cpp index e51f63d01b..b7861b56c8 100644 --- a/interface/src/InterfaceDynamicFactory.cpp +++ b/interface/src/InterfaceDynamicFactory.cpp @@ -87,10 +87,8 @@ EntityDynamicPointer InterfaceDynamicFactory::factoryBA(EntityItemPointer ownerE if (dynamic) { dynamic->deserialize(data); if (dynamic->lifetimeIsOver()) { - static QString repeatedMessage = - LogHandler::getInstance().addRepeatedMessageRegex(".*factoryBA lifetimeIsOver during dynamic creation.*"); - qDebug() << "InterfaceDynamicFactory::factoryBA lifetimeIsOver during dynamic creation --" - << dynamic->getExpires() << "<" << usecTimestampNow(); + HIFI_FDEBUG("InterfaceDynamicFactory::factoryBA lifetimeIsOver during dynamic creation --" + << dynamic->getExpires() << "<" << usecTimestampNow()); return nullptr; } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index df7546cd33..83199f5ba7 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -683,11 +683,12 @@ Menu::Menu() { qApp, SLOT(enablePerfStats(bool))); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandSimulationTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPhysicsTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarSimulateTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPhysicsSimulationTiming, 0, false); addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer); addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4d418a16d2..bba70a6a89 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -105,7 +105,8 @@ namespace MenuOption { const QString ExpandMyAvatarTiming = "Expand /myAvatar"; const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; const QString ExpandPaintGLTiming = "Expand /paintGL"; - const QString ExpandPhysicsSimulationTiming = "Expand /physics"; + const QString ExpandSimulationTiming = "Expand /simulation"; + const QString ExpandPhysicsTiming = "Expand /physics"; const QString ExpandUpdateTiming = "Expand /update"; const QString FirstPerson = "First Person"; const QString FirstPersonHMD = "Enter First Person Mode in HMD"; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 64ee492716..d54faf8b28 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -60,8 +60,8 @@ Stats::Stats(QQuickItem* parent) : QQuickItem(parent) { bool Stats::includeTimingRecord(const QString& name) { if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails)) { if (name.startsWith("/idle/update/")) { - if (name.startsWith("/idle/update/physics/")) { - return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming); + if (name.startsWith("/idle/update/simulation/")) { + return Menu::getInstance()->isOptionChecked(MenuOption::ExpandSimulationTiming); } else if (name.startsWith("/idle/update/myAvatar/")) { if (name.startsWith("/idle/update/myAvatar/simulate/")) { return Menu::getInstance()->isOptionChecked(MenuOption::ExpandMyAvatarSimulateTiming); @@ -75,8 +75,8 @@ bool Stats::includeTimingRecord(const QString& name) { return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming); } else if (name.startsWith("/paintGL/")) { return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming); - } else if (name.startsWith("step/")) { - return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming); + } else if (name.startsWith("physics/")) { + return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsTiming); } return true; } @@ -479,7 +479,14 @@ void Stats::updateStats(bool force) { float dt = (float)itr.value().getMovingAverage() / (float)USECS_PER_MSEC; _gameUpdateStats = QString("/idle/update = %1 ms").arg(dt); - QVector categories = { "devices", "physics", "otherAvatars", "MyAvatar", "misc" }; + QVector categories = { + "devices", + "MyAvatar", + "otherAvatars", + "pickManager", + "pointerManager", + "simulation" + }; for (int32_t j = 0; j < categories.size(); ++j) { QString recordKey = "/idle/update/" + categories[j]; itr = allRecords.find(recordKey); diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 59b3e874d7..518fdd3c17 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -39,9 +39,6 @@ AudioRingBufferTemplate::AudioRingBufferTemplate(int numFrameSamples, int num _nextOutput = _buffer; _endOfLastWrite = _buffer; } - - static QString repeatedOverflowMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG); - static QString repeatedDroppedMessage = LogHandler::getInstance().addRepeatedMessageRegex(DROPPED_SILENT_DEBUG); } template @@ -154,6 +151,11 @@ int AudioRingBufferTemplate::appendData(char *data, int maxSize) { return numReadSamples * SampleSize; } +namespace { + int repeatedOverflowMessageID = 0; + std::once_flag messageIDFlag; +} + template int AudioRingBufferTemplate::writeData(const char* data, int maxSize) { // only copy up to the number of samples we have capacity for @@ -167,7 +169,9 @@ int AudioRingBufferTemplate::writeData(const char* data, int maxSize) { _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete); _overflowCount++; - qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG); + std::call_once(messageIDFlag, [](int* id) { *id = LogHandler::getInstance().newRepeatedMessageID(); }, + &repeatedOverflowMessageID); + HIFI_FCDEBUG_ID(audio(), repeatedOverflowMessageID, RING_BUFFER_OVERFLOW_DEBUG); } if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) { @@ -224,7 +228,7 @@ int AudioRingBufferTemplate::addSilentSamples(int silentSamples) { if (numWriteSamples > samplesRoomFor) { numWriteSamples = samplesRoomFor; - qCDebug(audio) << qPrintable(DROPPED_SILENT_DEBUG); + HIFI_FCDEBUG(audio(), DROPPED_SILENT_DEBUG); } if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) { @@ -275,7 +279,10 @@ int AudioRingBufferTemplate::writeSamples(ConstIterator source, int maxSample int samplesToDelete = samplesToCopy - samplesRoomFor; _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete); _overflowCount++; - qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG); + + std::call_once(messageIDFlag, [](int* id) { *id = LogHandler::getInstance().newRepeatedMessageID(); }, + &repeatedOverflowMessageID); + HIFI_FCDEBUG_ID(audio(), repeatedOverflowMessageID, RING_BUFFER_OVERFLOW_DEBUG); } Sample* bufferLast = _buffer + _bufferLength - 1; @@ -297,7 +304,10 @@ int AudioRingBufferTemplate::writeSamplesWithFade(ConstIterator source, int m int samplesToDelete = samplesToCopy - samplesRoomFor; _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete); _overflowCount++; - qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG); + + std::call_once(messageIDFlag, [](int* id) { *id = LogHandler::getInstance().newRepeatedMessageID(); }, + &repeatedOverflowMessageID); + HIFI_FCDEBUG_ID(audio(), repeatedOverflowMessageID, RING_BUFFER_OVERFLOW_DEBUG); } Sample* bufferLast = _buffer + _bufferLength - 1; diff --git a/libraries/audio/src/PositionalAudioStream.cpp b/libraries/audio/src/PositionalAudioStream.cpp index 49b34a894e..a6bbc71a65 100644 --- a/libraries/audio/src/PositionalAudioStream.cpp +++ b/libraries/audio/src/PositionalAudioStream.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -80,10 +81,7 @@ int PositionalAudioStream::parsePositionalData(const QByteArray& positionalByteA // if the client sends us a bad position, flag it so that we don't consider this stream for mixing if (glm::isnan(_position.x) || glm::isnan(_position.y) || glm::isnan(_position.z)) { - static const QString INVALID_POSITION_REGEX = "PositionalAudioStream unpacked invalid position for node"; - static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(INVALID_POSITION_REGEX); - - qDebug() << "PositionalAudioStream unpacked invalid position for node" << uuidStringWithoutCurlyBraces(getNodeID()); + HIFI_FDEBUG("PositionalAudioStream unpacked invalid position for node" << uuidStringWithoutCurlyBraces(getNodeID()) ); _hasValidPosition = false; } else { diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index cfdfb1fc21..cb69d3a514 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -56,10 +56,8 @@ glm::mat4 StereoDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& basePr static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; -std::vector _screenActions; bool StereoDisplayPlugin::internalActivate() { auto screens = qApp->screens(); - _screenActions.resize(screens.size()); for (int i = 0; i < screens.size(); ++i) { auto screen = screens.at(i); QString name = QString("Screen %1: %2").arg(i + 1).arg(screen->name()); @@ -67,9 +65,9 @@ bool StereoDisplayPlugin::internalActivate() { if (screen == qApp->primaryScreen()) { checked = true; } - auto action = _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), name, - [this](bool clicked) { updateScreen(); }, true, checked, "Screens"); - _screenActions[i] = action; + const uint32_t screenIndex = i; + _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), name, + [=](bool clicked) { updateScreen(screenIndex); }, true, checked, "Screens"); } _container->removeMenu(FRAMERATE); @@ -80,18 +78,12 @@ bool StereoDisplayPlugin::internalActivate() { return Parent::internalActivate(); } -void StereoDisplayPlugin::updateScreen() { - for (uint32_t i = 0; i < _screenActions.size(); ++i) { - if (_screenActions[i]->isChecked()) { - _screen = qApp->screens().at(i); - _container->setFullscreen(_screen); - break; - } - } +void StereoDisplayPlugin::updateScreen(uint32_t i) { + _screen = qApp->screens().at(i); + _container->setFullscreen(_screen); } void StereoDisplayPlugin::internalDeactivate() { - _screenActions.clear(); _container->unsetFullscreen(); Parent::internalDeactivate(); } diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index c4205ea1db..5a7ca24059 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -31,7 +31,7 @@ public: protected: virtual bool internalActivate() override; virtual void internalDeactivate() override; - void updateScreen(); + void updateScreen(uint32_t i); float _ipd{ 0.064f }; QScreen* _screen; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 8a87a98a10..2e3a6c9552 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2193,10 +2193,8 @@ void EntityItem::deserializeActionsInternal() { entity->addActionInternal(simulation, action); updated << actionID; } else { - static QString repeatedMessage = - LogHandler::getInstance().addRepeatedMessageRegex(".*action creation failed for.*"); - qCDebug(entities) << "EntityItem::deserializeActionsInternal -- action creation failed for" - << getID() << _name; // getName(); + HIFI_FCDEBUG(entities(), "EntityItem::deserializeActionsInternal -- action creation failed for" + << getID() << _name); // getName(); removeActionInternal(actionID, nullptr); } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index c6d42a4a1e..4d0211a1b7 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -112,6 +112,11 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) { resetClientEditStats(); clearDeletedEntities(); + + { + QWriteLocker locker(&_needsParentFixupLock); + _needsParentFixup.clear(); + } } void EntityTree::readBitstreamToTree(const unsigned char* bitstream, @@ -1645,11 +1650,9 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } } else { - static QString repeatedMessage = - LogHandler::getInstance().addRepeatedMessageRegex("^Edit failed.*"); - qCDebug(entities) << "Edit failed. [" << message.getType() <<"] " << + HIFI_FCDEBUG(entities(), "Edit failed. [" << message.getType() <<"] " << "entity id:" << entityItemID << - "existingEntity pointer:" << existingEntity.get(); + "existingEntity pointer:" << existingEntity.get()); } } diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index b0e2faa600..e8365e38b7 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -566,20 +566,20 @@ glm::vec3 FBXReader::normalizeDirForPacking(const glm::vec3& dir) { } void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { - static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*"); - unsigned int totalSourceIndices = 0; foreach(const FBXMeshPart& part, extractedMesh.parts) { totalSourceIndices += (part.quadTrianglesIndices.size() + part.triangleIndices.size()); } + static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID(); + if (!totalSourceIndices) { - qCDebug(modelformat) << "buildModelMesh failed -- no indices, url = " << url; + HIFI_FCDEBUG_ID(modelformat(), repeatMessageID, "buildModelMesh failed -- no indices, url = " << url); return; } if (extractedMesh.vertices.size() == 0) { - qCDebug(modelformat) << "buildModelMesh failed -- no vertices, url = " << url; + HIFI_FCDEBUG_ID(modelformat(), repeatMessageID, "buildModelMesh failed -- no vertices, url = " << url); return; } diff --git a/libraries/gl/src/gl/GLWidget.cpp b/libraries/gl/src/gl/GLWidget.cpp index c1d049f7f3..1c0ad1a85e 100644 --- a/libraries/gl/src/gl/GLWidget.cpp +++ b/libraries/gl/src/gl/GLWidget.cpp @@ -72,6 +72,10 @@ void GLWidget::createContext() { _context->doneCurrent(); } +void GLWidget::swapBuffers() { + _context->swapBuffers(); +} + bool GLWidget::makeCurrent() { gl::Context::makeCurrent(_context->qglContext(), windowHandle()); return _context->makeCurrent(); diff --git a/libraries/gl/src/gl/GLWidget.h b/libraries/gl/src/gl/GLWidget.h index 21dffc1b75..a0bf8ea0b0 100644 --- a/libraries/gl/src/gl/GLWidget.h +++ b/libraries/gl/src/gl/GLWidget.h @@ -32,6 +32,7 @@ public: void createContext(); bool makeCurrent(); void doneCurrent(); + void swapBuffers(); gl::Context* context() { return _context; } QOpenGLContext* qglContext(); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 0803e380f2..fa934b5539 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -277,13 +277,8 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe emit dataReceived(sendingNodeType, packet.getPayloadSize()); return true; } else { - static const QString UNSOLICITED_REPLICATED_REGEX = - "Replicated packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown upstream"; - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex(UNSOLICITED_REPLICATED_REGEX); - - qCDebug(networking) << "Replicated packet of type" << headerType - << "received from unknown upstream" << packet.getSenderSockAddr(); + HIFI_FCDEBUG(networking(), "Replicated packet of type" << headerType + << "received from unknown upstream" << packet.getSenderSockAddr()); return false; } @@ -345,12 +340,8 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe return true; } else { - static const QString UNKNOWN_REGEX = "Packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown node with UUID"; - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex(UNKNOWN_REGEX); - - qCDebug(networking) << "Packet of type" << headerType - << "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID); + HIFI_FCDEBUG(networking(), + "Packet of type" << headerType << "received from unknown node with UUID" << uuidStringWithoutCurlyBraces(sourceID)); } } diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 13931be2ac..ddc4df1226 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -90,8 +90,8 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) // assume that we may need to send a new DS check in anytime a new keypair is generated connect(accountManager.data(), &AccountManager::newKeypair, this, &NodeList::sendDomainServerCheckIn); - // clear out NodeList when login is finished - connect(accountManager.data(), SIGNAL(loginComplete(const QUrl&)) , this, SLOT(reset())); + // clear out NodeList when login is finished and we know our new username + connect(accountManager.data(), SIGNAL(usernameChanged(QString)) , this, SLOT(reset())); // clear our NodeList when logout is requested connect(accountManager.data(), SIGNAL(logoutComplete()) , this, SLOT(reset())); diff --git a/libraries/networking/src/SequenceNumberStats.cpp b/libraries/networking/src/SequenceNumberStats.cpp index 6d7b271606..7f1ee39554 100644 --- a/libraries/networking/src/SequenceNumberStats.cpp +++ b/libraries/networking/src/SequenceNumberStats.cpp @@ -89,10 +89,7 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui } else if (absGap > MAX_REASONABLE_SEQUENCE_GAP) { arrivalInfo._status = Unreasonable; - static const QString UNREASONABLE_SEQUENCE_REGEX { "unreasonable sequence number: \\d+ previous: \\d+" }; - static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(UNREASONABLE_SEQUENCE_REGEX); - - qCDebug(networking) << "unreasonable sequence number:" << incoming << "previous:" << _lastReceivedSequence; + HIFI_FCDEBUG(networking(), "unreasonable sequence number:" << incoming << "previous:" << _lastReceivedSequence); _stats._unreasonable++; @@ -154,10 +151,7 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui arrivalInfo._status = Unreasonable; - static const QString UNREASONABLE_SEQUENCE_REGEX { "unreasonable sequence number: \\d+ \\(possible duplicate\\)" }; - static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(UNREASONABLE_SEQUENCE_REGEX); - - qCDebug(networking) << "unreasonable sequence number:" << incoming << "(possible duplicate)"; + HIFI_FCDEBUG(networking(), "unreasonable sequence number:" << incoming << "(possible duplicate)"); _stats._unreasonable++; diff --git a/libraries/networking/src/udt/Packet.cpp b/libraries/networking/src/udt/Packet.cpp index f07ea63994..0456fa1223 100644 --- a/libraries/networking/src/udt/Packet.cpp +++ b/libraries/networking/src/udt/Packet.cpp @@ -107,8 +107,7 @@ Packet::Packet(std::unique_ptr data, qint64 size, const HifiSockAddr& se QString::number(getMessagePartNumber())); } - static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("^Unobfuscating packet .*"); - qCDebug(networking) << qPrintable(debugString); + HIFI_FCDEBUG(networking(), debugString); #endif obfuscate(NoObfuscation); // Undo obfuscation diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index b62624aab9..0df54d539d 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -479,8 +479,7 @@ bool SendQueue::maybeResendPacket() { debugString = debugString.arg(QString::number(resendPacket.getMessageNumber()), QString::number(resendPacket.getMessagePartNumber())); } - static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("^Obfuscating packet .*"); - qCritical() << qPrintable(debugString); + HIFI_FDEBUG(debugString); #endif // Create copy of the packet diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 4189cb613c..4d4303698b 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -229,11 +229,7 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc if (bytesWritten < 0) { // when saturating a link this isn't an uncommon message - suppress it so it doesn't bomb the debug - static const QString WRITE_ERROR_REGEX = "Socket::writeDatagram QAbstractSocket::NetworkError - Unable to send a message"; - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex(WRITE_ERROR_REGEX); - - qCDebug(networking) << "Socket::writeDatagram" << _udpSocket.error() << "-" << qPrintable(_udpSocket.errorString()); + HIFI_FCDEBUG(networking(), "Socket::writeDatagram" << _udpSocket.error() << "-" << qPrintable(_udpSocket.errorString()) ); } return bytesWritten; @@ -517,11 +513,7 @@ std::vector Socket::getConnectionSockAddrs() { } void Socket::handleSocketError(QAbstractSocket::SocketError socketError) { - static const QString SOCKET_REGEX = "udt::Socket error - "; - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex(SOCKET_REGEX); - - qCDebug(networking) << "udt::Socket error - " << socketError << _udpSocket.errorString(); + HIFI_FCDEBUG(networking(), "udt::Socket error - " << socketError << _udpSocket.errorString()); } void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) { diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index dafdfd5bf4..e34ba34c8f 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -121,11 +121,7 @@ void Octree::recurseTreeWithPostOperation(const RecurseOctreeOperation& operatio void Octree::recurseElementWithOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex( - "Octree::recurseElementWithOperation\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - - qCDebug(octree) << "Octree::recurseElementWithOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; + HIFI_FCDEBUG(octree(), "Octree::recurseElementWithOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); return; } @@ -143,11 +139,7 @@ void Octree::recurseElementWithOperation(const OctreeElementPointer& element, co void Octree::recurseElementWithPostOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex( - "Octree::recurseElementWithPostOperation\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - - qCDebug(octree) << "Octree::recurseElementWithPostOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; + HIFI_FCDEBUG(octree(), "Octree::recurseElementWithPostOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); return; } @@ -173,11 +165,7 @@ void Octree::recurseElementWithOperationDistanceSorted(const OctreeElementPointe const glm::vec3& point, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex( - "Octree::recurseElementWithOperationDistanceSorted\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - - qCDebug(octree) << "Octree::recurseElementWithOperationDistanceSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; + HIFI_FCDEBUG(octree(), "Octree::recurseElementWithOperationDistanceSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); return; } @@ -215,11 +203,7 @@ void Octree::recurseTreeWithOperator(RecurseOctreeOperator* operatorObject) { bool Octree::recurseElementWithOperator(const OctreeElementPointer& element, RecurseOctreeOperator* operatorObject, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex( - "Octree::recurseElementWithOperator\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - - qCDebug(octree) << "Octree::recurseElementWithOperator() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; + HIFI_FCDEBUG(octree(), "Octree::recurseElementWithOperator() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); return false; } @@ -285,11 +269,7 @@ OctreeElementPointer Octree::createMissingElement(const OctreeElementPointer& la const unsigned char* codeToReach, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex( - "Octree::createMissingElement\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - - qCDebug(octree) << "Octree::createMissingElement() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; + HIFI_FCDEBUG(octree(), "Octree::createMissingElement() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); return lastParentElement; } int indexOfNewChild = branchIndexWithDescendant(lastParentElement->getOctalCode(), codeToReach); @@ -446,16 +426,9 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, uint64_t buffe (unsigned char *)bitstreamAt, NULL); int numberOfThreeBitSectionsInStream = numberOfThreeBitSectionsInCode(bitstreamAt, bufferSizeBytes); if (numberOfThreeBitSectionsInStream > UNREASONABLY_DEEP_RECURSION) { - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex( - "UNEXPECTED: parsing of the octal code would make UNREASONABLY_DEEP_RECURSION... " - "numberOfThreeBitSectionsInStream: \\d+ This buffer is corrupt. Returning." - ); - - - qCDebug(octree) << "UNEXPECTED: parsing of the octal code would make UNREASONABLY_DEEP_RECURSION... " + HIFI_FCDEBUG(octree(), "UNEXPECTED: parsing of the octal code would make UNREASONABLY_DEEP_RECURSION... " "numberOfThreeBitSectionsInStream:" << numberOfThreeBitSectionsInStream << - "This buffer is corrupt. Returning."; + "This buffer is corrupt. Returning."); return; } diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 989951b661..ef45e8b7ba 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -401,11 +401,7 @@ OctreeElementPointer OctreeElement::addChildAtIndex(int childIndex) { bool OctreeElement::safeDeepDeleteChildAtIndex(int childIndex, int recursionCount) { bool deleteApproved = false; if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex( - "OctreeElement::safeDeepDeleteChildAtIndex\\(\\) reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - - qCDebug(octree) << "OctreeElement::safeDeepDeleteChildAtIndex() reached DANGEROUSLY_DEEP_RECURSION, bailing!"; + HIFI_FCDEBUG(octree(), "OctreeElement::safeDeepDeleteChildAtIndex() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); return deleteApproved; } OctreeElementPointer childToDelete = getChildAtIndex(childIndex); diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index 054603440d..117f3e0385 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -636,12 +636,8 @@ void OctreeSceneStats::trackIncomingOctreePacket(ReceivedMessage& message, bool const qint64 MAX_RESONABLE_FLIGHT_TIME = 200 * USECS_PER_SECOND; // 200 seconds is more than enough time for a packet to arrive const qint64 MIN_RESONABLE_FLIGHT_TIME = -1 * (qint64)USECS_PER_SECOND; // more than 1 second of "reverse flight time" would be unreasonable if (flightTime > MAX_RESONABLE_FLIGHT_TIME || flightTime < MIN_RESONABLE_FLIGHT_TIME) { - static QString repeatedMessage - = LogHandler::getInstance().addRepeatedMessageRegex( - "ignoring unreasonable packet... flightTime: -?\\d+ nodeClockSkewUsec: -?\\d+ usecs"); - - qCDebug(octree) << "ignoring unreasonable packet... flightTime:" << flightTime - << "nodeClockSkewUsec:" << nodeClockSkewUsec << "usecs";; + HIFI_FCDEBUG(octree(), "ignoring unreasonable packet... flightTime:" << flightTime + << "nodeClockSkewUsec:" << nodeClockSkewUsec << "usecs"); return; // ignore any packets that are unreasonable } diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 807acbfe80..a542c61d43 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -132,7 +132,7 @@ protected: // // (2) For locally owned simulation: we store the last values sent to the server, integrated forward over time // according to how we think the server doing it. We calculate the error between the true local transform - // and the remote to decide when to send another update. + // and the remote to decide whether to send another update or not. // glm::vec3 _serverPosition; // in simulation-frame (not world-frame) glm::quat _serverRotation; diff --git a/libraries/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp index bc68d6de73..9b2da22665 100644 --- a/libraries/physics/src/ObjectActionTractor.cpp +++ b/libraries/physics/src/ObjectActionTractor.cpp @@ -47,19 +47,22 @@ ObjectActionTractor::~ObjectActionTractor() { bool ObjectActionTractor::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position, glm::vec3& linearVelocity, glm::vec3& angularVelocity, float& linearTimeScale, float& angularTimeScale) { - SpatiallyNestablePointer other = getOther(); + bool success { true }; + EntityItemPointer other = std::dynamic_pointer_cast(getOther()); withReadLock([&]{ linearTimeScale = _linearTimeScale; angularTimeScale = _angularTimeScale; if (!_otherID.isNull()) { - if (other) { + if (other && other->isReadyToComputeShape()) { rotation = _desiredRotationalTarget * other->getWorldOrientation(); position = other->getWorldOrientation() * _desiredPositionalTarget + other->getWorldPosition(); } else { - // we should have an "other" but can't find it, so disable the tractor. + // we should have an "other" but can't find it, or its collision shape isn't loaded, + // so disable the tractor. linearTimeScale = FLT_MAX; angularTimeScale = FLT_MAX; + success = false; } } else { rotation = _desiredRotationalTarget; @@ -68,7 +71,7 @@ bool ObjectActionTractor::getTarget(float deltaTimeStep, glm::quat& rotation, gl linearVelocity = glm::vec3(); angularVelocity = glm::vec3(); }); - return true; + return success; } bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { @@ -122,6 +125,8 @@ bool ObjectActionTractor::prepareForTractorUpdate(btScalar deltaTimeStep) { linearTractorCount++; position += positionForAction; } + } else { + return false; // we don't have both entities loaded, so don't do anything } } diff --git a/libraries/physics/src/ObjectConstraintBallSocket.cpp b/libraries/physics/src/ObjectConstraintBallSocket.cpp index 70613d46ae..4736f2c9e2 100644 --- a/libraries/physics/src/ObjectConstraintBallSocket.cpp +++ b/libraries/physics/src/ObjectConstraintBallSocket.cpp @@ -85,12 +85,11 @@ btTypedConstraint* ObjectConstraintBallSocket::getConstraint() { return constraint; } - static QString repeatedBallSocketNoRigidBody = LogHandler::getInstance().addRepeatedMessageRegex( - "ObjectConstraintBallSocket::getConstraint -- no rigidBody.*"); + static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID(); btRigidBody* rigidBodyA = getRigidBody(); if (!rigidBodyA) { - qCDebug(physics) << "ObjectConstraintBallSocket::getConstraint -- no rigidBodyA"; + HIFI_FCDEBUG_ID(physics(), repeatMessageID, "ObjectConstraintBallSocket::getConstraint -- no rigidBodyA"); return nullptr; } @@ -99,7 +98,7 @@ btTypedConstraint* ObjectConstraintBallSocket::getConstraint() { btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); if (!rigidBodyB) { - qCDebug(physics) << "ObjectConstraintBallSocket::getConstraint -- no rigidBodyB"; + HIFI_FCDEBUG_ID(physics(), repeatMessageID, "ObjectConstraintBallSocket::getConstraint -- no rigidBodyB"); return nullptr; } diff --git a/libraries/physics/src/ObjectConstraintConeTwist.cpp b/libraries/physics/src/ObjectConstraintConeTwist.cpp index 86f1f21c63..47228c1c16 100644 --- a/libraries/physics/src/ObjectConstraintConeTwist.cpp +++ b/libraries/physics/src/ObjectConstraintConeTwist.cpp @@ -96,12 +96,11 @@ btTypedConstraint* ObjectConstraintConeTwist::getConstraint() { return constraint; } - static QString repeatedConeTwistNoRigidBody = LogHandler::getInstance().addRepeatedMessageRegex( - "ObjectConstraintConeTwist::getConstraint -- no rigidBody.*"); + static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID(); btRigidBody* rigidBodyA = getRigidBody(); if (!rigidBodyA) { - qCDebug(physics) << "ObjectConstraintConeTwist::getConstraint -- no rigidBodyA"; + HIFI_FCDEBUG_ID(physics(), repeatMessageID, "ObjectConstraintConeTwist::getConstraint -- no rigidBodyA"); return nullptr; } @@ -130,7 +129,7 @@ btTypedConstraint* ObjectConstraintConeTwist::getConstraint() { btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); if (!rigidBodyB) { - qCDebug(physics) << "ObjectConstraintConeTwist::getConstraint -- no rigidBodyB"; + HIFI_FCDEBUG_ID(physics(), repeatMessageID, "ObjectConstraintConeTwist::getConstraint -- no rigidBodyB"); return nullptr; } diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 99ddd45abd..4793741391 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -94,13 +94,12 @@ btTypedConstraint* ObjectConstraintHinge::getConstraint() { if (constraint) { return constraint; } - - static QString repeatedHingeNoRigidBody = LogHandler::getInstance().addRepeatedMessageRegex( - "ObjectConstraintHinge::getConstraint -- no rigidBody.*"); + + static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID(); btRigidBody* rigidBodyA = getRigidBody(); if (!rigidBodyA) { - qCDebug(physics) << "ObjectConstraintHinge::getConstraint -- no rigidBodyA"; + HIFI_FCDEBUG_ID(physics(), repeatMessageID, "ObjectConstraintHinge::getConstraint -- no rigidBodyA"); return nullptr; } @@ -115,7 +114,7 @@ btTypedConstraint* ObjectConstraintHinge::getConstraint() { // This hinge is between two entities... find the other rigid body. btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); if (!rigidBodyB) { - qCDebug(physics) << "ObjectConstraintHinge::getConstraint -- no rigidBodyB"; + HIFI_FCDEBUG_ID(physics(), repeatMessageID, "ObjectConstraintHinge::getConstraint -- no rigidBodyB"); return nullptr; } diff --git a/libraries/physics/src/ObjectConstraintSlider.cpp b/libraries/physics/src/ObjectConstraintSlider.cpp index c236afc10d..da5bba7f4d 100644 --- a/libraries/physics/src/ObjectConstraintSlider.cpp +++ b/libraries/physics/src/ObjectConstraintSlider.cpp @@ -87,12 +87,11 @@ btTypedConstraint* ObjectConstraintSlider::getConstraint() { return constraint; } - static QString repeatedSliderNoRigidBody = LogHandler::getInstance().addRepeatedMessageRegex( - "ObjectConstraintSlider::getConstraint -- no rigidBody.*"); + static int repeatMessageID = LogHandler::getInstance().newRepeatedMessageID(); btRigidBody* rigidBodyA = getRigidBody(); if (!rigidBodyA) { - qCDebug(physics) << "ObjectConstraintSlider::getConstraint -- no rigidBodyA"; + HIFI_FCDEBUG_ID(physics(), repeatMessageID, "ObjectConstraintSlider::getConstraint -- no rigidBodyA"); return nullptr; } @@ -121,7 +120,7 @@ btTypedConstraint* ObjectConstraintSlider::getConstraint() { btRigidBody* rigidBodyB = getOtherRigidBody(otherEntityID); if (!rigidBodyB) { - qCDebug(physics) << "ObjectConstraintSlider::getConstraint -- no rigidBodyB"; + HIFI_FCDEBUG_ID(physics(), repeatMessageID, "ObjectConstraintSlider::getConstraint -- no rigidBodyB"); return nullptr; } diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 06e7069f72..ab7c2ec252 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -41,7 +41,7 @@ void PhysicalEntitySimulation::init( // begin EntitySimulation overrides void PhysicalEntitySimulation::updateEntitiesInternal(uint64_t now) { - // Do nothing here because the "internal" update the PhysicsEngine::stepSimualtion() which is done elsewhere. + // Do nothing here because the "internal" update the PhysicsEngine::stepSimulation() which is done elsewhere. } void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) { diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 1d4c385f07..7c2ad55405 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -413,7 +413,7 @@ void PhysicsEngine::harvestPerformanceStats() { if (QString(itr->Get_Current_Name()) == "stepSimulation") { itr->Enter_Child(childIndex); StatsHarvester harvester; - harvester.recurse(itr, "step/"); + harvester.recurse(itr, "physics/"); break; } itr->Next(); diff --git a/libraries/render-utils/src/BackgroundStage.cpp b/libraries/render-utils/src/BackgroundStage.cpp index 886795ec79..d9115c7943 100644 --- a/libraries/render-utils/src/BackgroundStage.cpp +++ b/libraries/render-utils/src/BackgroundStage.cpp @@ -126,9 +126,6 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, if (defaultSkyboxAmbientTexture) { sceneKeyLight->setAmbientSphere(defaultSkyboxAmbientTexture->getIrradiance()); sceneKeyLight->setAmbientMap(defaultSkyboxAmbientTexture); - } else { - static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex( - "Failed to get a valid Default Skybox Ambient Texture ? probably because it couldn't be find during initialization step"); } // fall through: render defaults skybox } else { diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index bdff97c1c1..8aabffea46 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -41,6 +41,11 @@ void render::renderItems(const RenderContextPointer& renderContext, const ItemBo } } +namespace { + int repeatedInvalidKeyMessageID = 0; + std::once_flag messageIDFlag; +} + void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, const Item& item, const ShapeKey& globalKey) { assert(item.getKey().isShape()); auto key = item.getShapeKey() | globalKey; @@ -55,9 +60,9 @@ void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, cons } else if (key.hasOwnPipeline()) { item.render(args); } else { - qCDebug(renderlogging) << "Item could not be rendered with invalid key" << key; - static QString repeatedCouldNotBeRendered = LogHandler::getInstance().addRepeatedMessageRegex( - "Item could not be rendered with invalid key.*"); + std::call_once(messageIDFlag, [](int* id) { *id = LogHandler::getInstance().newRepeatedMessageID(); }, + &repeatedInvalidKeyMessageID); + HIFI_FCDEBUG_ID(renderlogging(), repeatedInvalidKeyMessageID, "Item could not be rendered with invalid key" << key); } args->_itemShapeKey = 0; } @@ -108,9 +113,9 @@ void render::renderStateSortShapes(const RenderContextPointer& renderContext, } else if (key.hasOwnPipeline()) { ownPipelineBucket.push_back( std::make_tuple(item, key) ); } else { - static QString repeatedCouldNotBeRendered = LogHandler::getInstance().addRepeatedMessageRegex( - "Item could not be rendered with invalid key.*"); - qCDebug(renderlogging) << "Item could not be rendered with invalid key" << key; + std::call_once(messageIDFlag, [](int* id) { *id = LogHandler::getInstance().newRepeatedMessageID(); }, + &repeatedInvalidKeyMessageID); + HIFI_FCDEBUG_ID(renderlogging(), repeatedInvalidKeyMessageID, "Item could not be rendered with invalid key" << key); } } } diff --git a/libraries/shared/src/LogHandler.cpp b/libraries/shared/src/LogHandler.cpp index 49927a325b..45cf01510d 100644 --- a/libraries/shared/src/LogHandler.cpp +++ b/libraries/shared/src/LogHandler.cpp @@ -25,7 +25,7 @@ #include #include -QMutex LogHandler::_mutex; +QMutex LogHandler::_mutex(QMutex::Recursive); LogHandler& LogHandler::getInstance() { static LogHandler staticInstance; @@ -36,6 +36,9 @@ LogHandler::LogHandler() { // when the log handler is first setup we should print our timezone QString timezoneString = "Time zone: " + QDateTime::currentDateTime().toString("t"); printMessage(LogMsgType::LogInfo, QMessageLogContext(), timezoneString); + + // make sure we setup the repeated message flusher, but do it on the LogHandler thread + QMetaObject::invokeMethod(this, "setupRepeatedMessageFlusher"); } LogHandler::~LogHandler() { @@ -91,58 +94,25 @@ void LogHandler::setShouldDisplayMilliseconds(bool shouldDisplayMilliseconds) { void LogHandler::flushRepeatedMessages() { QMutexLocker lock(&_mutex); - for(auto& message: _repeatedMessages) { - if (message.messageCount > 1) { - QString repeatMessage = QString("%1 repeated log entries matching \"%2\" - Last entry: \"%3\"") - .arg(message.messageCount - 1) - .arg(message.regexp.pattern()) - .arg(message.lastMessage); - - QMessageLogContext emptyContext; - lock.unlock(); - printMessage(LogSuppressed, emptyContext, repeatMessage); - lock.relock(); + // New repeat-suppress scheme: + for (int m = 0; m < (int)_repeatedMessageRecords.size(); ++m) { + int repeatCount = _repeatedMessageRecords[m].repeatCount; + if (repeatCount > 1) { + QString repeatLogMessage = QString().setNum(repeatCount) + " repeated log entries - Last entry: \"" + + _repeatedMessageRecords[m].repeatString + "\""; + printMessage(LogSuppressed, QMessageLogContext(), repeatLogMessage); + _repeatedMessageRecords[m].repeatCount = 0; + _repeatedMessageRecords[m].repeatString = QString(); } - - message.messageCount = 0; } } QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& context, const QString& message) { - QMutexLocker lock(&_mutex); if (message.isEmpty()) { return QString(); } - - if (type == LogDebug) { - // for debug messages, check if this matches any of our regexes for repeated log messages - for (auto& repeatRegex : _repeatedMessages) { - if (repeatRegex.regexp.indexIn(message) != -1) { - // If we've printed the first one then return out. - if (repeatRegex.messageCount++ == 0) { - break; - } - repeatRegex.lastMessage = message; - return QString(); - } - } - } - - if (type == LogDebug) { - // see if this message is one we should only print once - for (auto& onceOnly : _onetimeMessages) { - if (onceOnly.regexp.indexIn(message) != -1) { - if (onceOnly.messageCount++ == 0) { - // we have a match and haven't yet printed this message. - break; - } else { - // We've already printed this message, don't print it again. - return QString(); - } - } - } - } + QMutexLocker lock(&_mutex); // log prefix is in the following format // [TIMESTAMP] [DEBUG] [PID] [TID] [TARGET] logged string @@ -199,21 +169,27 @@ void LogHandler::setupRepeatedMessageFlusher() { }); } -const QString& LogHandler::addRepeatedMessageRegex(const QString& regexString) { - // make sure we setup the repeated message flusher, but do it on the LogHandler thread - QMetaObject::invokeMethod(this, "setupRepeatedMessageFlusher"); - +int LogHandler::newRepeatedMessageID() { QMutexLocker lock(&_mutex); - RepeatedMessage repeatRecord; - repeatRecord.regexp = QRegExp(regexString); - _repeatedMessages.push_back(repeatRecord); - return regexString; + int newMessageId = _currentMessageID; + ++_currentMessageID; + RepeatedMessageRecord newRecord { 0, QString() }; + _repeatedMessageRecords.push_back(newRecord); + return newMessageId; } -const QString& LogHandler::addOnlyOnceMessageRegex(const QString& regexString) { +void LogHandler::printRepeatedMessage(int messageID, LogMsgType type, const QMessageLogContext& context, + const QString& message) { QMutexLocker lock(&_mutex); - OnceOnlyMessage onetimeMessage; - onetimeMessage.regexp = QRegExp(regexString); - _onetimeMessages.push_back(onetimeMessage); - return regexString; + if (messageID >= _currentMessageID) { + return; + } + + if (_repeatedMessageRecords[messageID].repeatCount == 0) { + printMessage(type, context, message); + } else { + _repeatedMessageRecords[messageID].repeatString = message; + } + + ++_repeatedMessageRecords[messageID].repeatCount; } diff --git a/libraries/shared/src/LogHandler.h b/libraries/shared/src/LogHandler.h index 2e64f16c1e..56450768ff 100644 --- a/libraries/shared/src/LogHandler.h +++ b/libraries/shared/src/LogHandler.h @@ -51,8 +51,8 @@ public: /// prints various process, message type, and time information static void verboseMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message); - const QString& addRepeatedMessageRegex(const QString& regexString); - const QString& addOnlyOnceMessageRegex(const QString& regexString); + int newRepeatedMessageID(); + void printRepeatedMessage(int messageID, LogMsgType type, const QMessageLogContext& context, const QString &message); private slots: void setupRepeatedMessageFlusher(); @@ -68,20 +68,40 @@ private: bool _shouldOutputThreadID { false }; bool _shouldDisplayMilliseconds { false }; - struct RepeatedMessage { - QRegExp regexp; - int messageCount { 0 }; - QString lastMessage; + int _currentMessageID { 0 }; + struct RepeatedMessageRecord { + int repeatCount; + QString repeatString; }; - std::vector _repeatedMessages; - - struct OnceOnlyMessage { - QRegExp regexp; - int messageCount { 0 }; - }; - std::vector _onetimeMessages; - + std::vector _repeatedMessageRecords; static QMutex _mutex; }; +#define HIFI_FCDEBUG(category, message) \ + do { \ + if (category.isDebugEnabled()) { \ + static int repeatedMessageID_ = LogHandler::getInstance().newRepeatedMessageID(); \ + QString logString_; \ + QDebug debugStringReceiver_(&logString_); \ + debugStringReceiver_ << message; \ + LogHandler::getInstance().printRepeatedMessage(repeatedMessageID_, LogDebug, QMessageLogContext(__FILE__, \ + __LINE__, __func__, category().categoryName()), logString_); \ + } \ + } while (false) + +#define HIFI_FDEBUG(message) HIFI_FCDEBUG((*QLoggingCategory::defaultCategory()), message) + +#define HIFI_FCDEBUG_ID(category, messageID, message) \ + do { \ + if (category.isDebugEnabled()) { \ + QString logString_; \ + QDebug debugStringReceiver_(&logString_); \ + debugStringReceiver_ << message; \ + LogHandler::getInstance().printRepeatedMessage(messageID, LogDebug, QMessageLogContext(__FILE__, \ + __LINE__, __func__, category().categoryName()), logString_); \ + } \ + } while (false) + +#define HIFI_FDEBUG_ID(messageID, message) HIFI_FCDEBUG_ID((*QLoggingCategory::defaultCategory()), messageID, message) + #endif // hifi_LogHandler_h diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp index 6d54351743..58dc971cb9 100644 --- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp +++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.cpp @@ -34,48 +34,140 @@ PluginContainer::~PluginContainer() { INSTANCE = nullptr; }; +struct MenuCache { + QSet menus; + struct Item { + QString path; + std::function onClicked; + bool checkable; + bool checked; + QString groupName; + }; + QHash items; + std::map _exclusiveGroups; + + void addMenu(ui::Menu* menu, const QString& menuName) { + if (!menu) { + menus.insert(menuName); + return; + } + + flushCache(menu); + menu->addMenu(menuName); + } + + void removeMenu(ui::Menu* menu, const QString& menuName) { + if (!menu) { + menus.remove(menuName); + return; + } + flushCache(menu); + menu->removeMenu(menuName); + } + + void addMenuItem(ui::Menu* menu, const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName) { + if (!menu) { + items[name] = Item{ path, onClicked, checkable, checked, groupName }; + return; + } + flushCache(menu); + MenuWrapper* parentItem = menu->getMenu(path); + QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name); + if (!groupName.isEmpty()) { + QActionGroup* group{ nullptr }; + if (!_exclusiveGroups.count(groupName)) { + group = _exclusiveGroups[groupName] = new QActionGroup(menu); + group->setExclusive(true); + } else { + group = _exclusiveGroups[groupName]; + } + group->addAction(action); + } + QObject::connect(action, &QAction::triggered, [=] { + onClicked(action->isChecked()); + }); + action->setCheckable(checkable); + action->setChecked(checked); + } + void removeMenuItem(ui::Menu* menu, const QString& menuName, const QString& menuItemName) { + if (!menu) { + items.remove(menuItemName); + return; + } + flushCache(menu); + menu->removeMenuItem(menuName, menuItemName); + } + + bool isOptionChecked(ui::Menu* menu, const QString& name) { + if (!menu) { + return items.contains(name) && items[name].checked; + } + flushCache(menu); + return menu->isOptionChecked(name); + } + + void setIsOptionChecked(ui::Menu* menu, const QString& name, bool checked) { + if (!menu) { + if (items.contains(name)) { + items[name].checked = checked; + } + return; + } + flushCache(menu); + + } + + void flushCache(ui::Menu* menu) { + if (!menu) { + return; + } + static bool flushed = false; + if (flushed) { + return; + } + flushed = true; + for (const auto& menuName : menus) { + addMenu(menu, menuName); + } + menus.clear(); + + for (const auto& menuItemName : items.keys()) { + const auto menuItem = items[menuItemName]; + addMenuItem(menu, menuItem.path, menuItemName, menuItem.onClicked, menuItem.checkable, menuItem.checked, menuItem.groupName); + } + items.clear(); + } +}; + + +static MenuCache& getMenuCache() { + static MenuCache cache; + return cache; +} void PluginContainer::addMenu(const QString& menuName) { - getPrimaryMenu()->addMenu(menuName); + getMenuCache().addMenu(getPrimaryMenu(), menuName); } void PluginContainer::removeMenu(const QString& menuName) { - getPrimaryMenu()->removeMenu(menuName); + getMenuCache().removeMenu(getPrimaryMenu(), menuName); } -QAction* PluginContainer::addMenuItem(PluginType type, const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName) { - auto menu = getPrimaryMenu(); - MenuWrapper* parentItem = menu->getMenu(path); - QAction* action = menu->addActionToQMenuAndActionHash(parentItem, name); - if (!groupName.isEmpty()) { - QActionGroup* group { nullptr }; - if (!_exclusiveGroups.count(groupName)) { - group = _exclusiveGroups[groupName] = new QActionGroup(menu); - group->setExclusive(true); - } else { - group = _exclusiveGroups[groupName]; - } - group->addAction(action); - } - QObject::connect(action, &QAction::triggered, [=] { - onClicked(action->isChecked()); - }); - action->setCheckable(checkable); - action->setChecked(checked); +void PluginContainer::addMenuItem(PluginType type, const QString& path, const QString& name, std::function onClicked, bool checkable, bool checked, const QString& groupName) { + getMenuCache().addMenuItem(getPrimaryMenu(), path, name, onClicked, checkable, checked, groupName); if (type == PluginType::DISPLAY_PLUGIN) { _currentDisplayPluginActions.push_back({ path, name }); } else { _currentInputPluginActions.push_back({ path, name }); } - return action; } void PluginContainer::removeMenuItem(const QString& menuName, const QString& menuItem) { - getPrimaryMenu()->removeMenuItem(menuName, menuItem); + getMenuCache().removeMenuItem(getPrimaryMenu(), menuName, menuItem); } bool PluginContainer::isOptionChecked(const QString& name) { - return getPrimaryMenu()->isOptionChecked(name); + return getMenuCache().isOptionChecked(getPrimaryMenu(), name); } void PluginContainer::setIsOptionChecked(const QString& path, bool checked) { @@ -161,3 +253,7 @@ void PluginContainer::setBoolSetting(const QString& settingName, bool value) { Setting::Handle settingValue(settingName, value); return settingValue.set(value); } + +void PluginContainer::flushMenuUpdates() { + getMenuCache().flushCache(getPrimaryMenu()); +} diff --git a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h index 167af100b3..da9ea46cf4 100644 --- a/libraries/ui-plugins/src/ui-plugins/PluginContainer.h +++ b/libraries/ui-plugins/src/ui-plugins/PluginContainer.h @@ -46,7 +46,7 @@ public: void addMenu(const QString& menuName); void removeMenu(const QString& menuName); - QAction* addMenuItem(PluginType pluginType, const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = ""); + void addMenuItem(PluginType pluginType, const QString& path, const QString& name, std::function onClicked, bool checkable = false, bool checked = false, const QString& groupName = ""); void removeMenuItem(const QString& menuName, const QString& menuItem); bool isOptionChecked(const QString& name); void setIsOptionChecked(const QString& path, bool checked); @@ -77,9 +77,9 @@ public: } protected: + void flushMenuUpdates(); QVector> _currentDisplayPluginActions; QVector> _currentInputPluginActions; - std::map _exclusiveGroups; QRect _savedGeometry { 10, 120, 800, 600 }; }; diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 4f041d3067..4002fd297b 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -7,11 +7,12 @@ /* jslint bitwise: true */ -/* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, +/* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true, LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, COLORS_GRAB_SEARCHING_HALF_SQUEEZE - COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, TRIGGER_ON_VALUE, PointerManager + COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, TRIGGER_ON_VALUE, PointerManager, print + Selection, DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE */ controllerDispatcherPlugins = {}; @@ -123,6 +124,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); return getControllerWorldLocation(Controller.Standard.RightHand, true); }; + Selection.enableListHighlight(DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE); + Selection.enableListToScene(DISPATCHER_HOVERING_LIST); + this.updateTimings = function () { _this.intervalCount++; var thisInterval = Date.now(); @@ -157,7 +161,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.update = function () { try { _this.updateInternal(); - } catch (e) { + } catch (e) { print(e); } Script.setTimeout(_this.update, BASIC_TIMER_INTERVAL_MS); @@ -477,6 +481,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Controller.disableMapping(MAPPING_NAME); _this.pointerManager.removePointers(); Pointers.removePointer(this.mouseRayPick); + Selection.disableListHighlight(DISPATCHER_HOVERING_LIST); }; } function mouseReleaseOnOverlay(overlayID, event) { diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 5807465abb..67fe9068b6 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -10,7 +10,7 @@ getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable, - cloneEntity, DISPATCHER_PROPERTIES, TEAR_AWAY_DISTANCE, Uuid + cloneEntity, DISPATCHER_PROPERTIES, TEAR_AWAY_DISTANCE, Uuid, unhighlightTargetEntity */ Script.include("/~/system/libraries/Xform.js"); @@ -488,7 +488,13 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.dropGestureReset(); this.clearEquipHaptics(); Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + unhighlightTargetEntity(this.targetEntityID); + var message = { + hand: this.hand, + entityID: this.targetEntityID + }; + Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message)); var grabbedProperties = Entities.getEntityProperties(this.targetEntityID); // if an object is "equipped" and has a predefined offset, use it. diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 1cf057338e..cc884cdbc7 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -14,7 +14,7 @@ PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI - Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection + Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -103,6 +103,7 @@ Script.include("/~/system/libraries/Xform.js"); this.contextOverlayTimer = false; this.previousCollisionStatus = false; this.locked = false; + this.highlightedEntity = null; this.reticleMinX = MARGIN; this.reticleMaxX; this.reticleMinY = MARGIN; @@ -402,6 +403,9 @@ Script.include("/~/system/libraries/Xform.js"); if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.notPointingAtEntity(controllerData) || this.targetIsNull()) { this.endNearGrabAction(); + Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", + this.highlightedEntity); + this.highlightedEntity = null; return makeRunningValues(false, [], []); } this.intersectionDistance = controllerData.rayPicks[this.hand].distance; @@ -450,7 +454,9 @@ Script.include("/~/system/libraries/Xform.js"); if (rayPickInfo.type === Picks.INTERSECTED_ENTITY) { if (controllerData.triggerClicks[this.hand]) { var entityID = rayPickInfo.objectID; - + Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", + this.highlightedEntity); + this.highlightedEntity = null; var targetProps = Entities.getEntityProperties(entityID, [ "dynamic", "shapeType", "position", "rotation", "dimensions", "density", @@ -498,38 +504,62 @@ Script.include("/~/system/libraries/Xform.js"); this.startFarGrabAction(controllerData, targetProps); } } - } else if (!this.entityWithContextOverlay) { - var _this = this; + } else { + var targetEntityID = rayPickInfo.objectID; + if (this.highlightedEntity !== targetEntityID) { + Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", + this.highlightedEntity); + var selectionTargetProps = Entities.getEntityProperties(targetEntityID, [ + "dynamic", "shapeType", "position", + "rotation", "dimensions", "density", + "userData", "locked", "type", "href" + ]); - if (_this.potentialEntityWithContextOverlay !== rayPickInfo.objectID) { - if (_this.contextOverlayTimer) { - Script.clearTimeout(_this.contextOverlayTimer); + var selectionTargetObject = new TargetObject(targetEntityID, selectionTargetProps); + selectionTargetObject.parentProps = getEntityParents(selectionTargetProps); + var selectionTargetEntity = selectionTargetObject.getTargetEntity(); + + if (entityIsGrabbable(selectionTargetEntity.props) || + entityIsGrabbable(selectionTargetObject.entityProps)) { + + Selection.addToSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", rayPickInfo.objectID); } - _this.contextOverlayTimer = false; - _this.potentialEntityWithContextOverlay = rayPickInfo.objectID; + this.highlightedEntity = rayPickInfo.objectID; } - if (!_this.contextOverlayTimer) { - _this.contextOverlayTimer = Script.setTimeout(function () { - if (!_this.entityWithContextOverlay && - _this.contextOverlayTimer && - _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { - var props = Entities.getEntityProperties(rayPickInfo.objectID); - var pointerEvent = { - type: "Move", - id: _this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, rayPickInfo.intersection, props), - pos3D: rayPickInfo.intersection, - normal: rayPickInfo.surfaceNormal, - direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal), - button: "Secondary" - }; - if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.objectID, pointerEvent)) { - _this.entityWithContextOverlay = rayPickInfo.objectID; - } + if (!this.entityWithContextOverlay) { + var _this = this; + + if (_this.potentialEntityWithContextOverlay !== rayPickInfo.objectID) { + if (_this.contextOverlayTimer) { + Script.clearTimeout(_this.contextOverlayTimer); } _this.contextOverlayTimer = false; - }, 500); + _this.potentialEntityWithContextOverlay = rayPickInfo.objectID; + } + + if (!_this.contextOverlayTimer) { + _this.contextOverlayTimer = Script.setTimeout(function () { + if (!_this.entityWithContextOverlay && + _this.contextOverlayTimer && + _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { + var props = Entities.getEntityProperties(rayPickInfo.objectID); + var pointerEvent = { + type: "Move", + id: _this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, rayPickInfo.intersection, props), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.surfaceNormal, + direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal), + button: "Secondary" + }; + if (ContextOverlay.createOrDestroyContextOverlay(rayPickInfo.objectID, pointerEvent)) { + _this.entityWithContextOverlay = rayPickInfo.objectID; + } + } + _this.contextOverlayTimer = false; + }, 500); + } } } } else if (this.distanceRotating) { @@ -545,6 +575,9 @@ Script.include("/~/system/libraries/Xform.js"); if (disableModule) { if (disableModule.disableModules) { this.endNearGrabAction(); + Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", + this.highlightedEntity); + this.highlightedEntity = null; return makeRunningValues(false, [], []); } } diff --git a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js new file mode 100644 index 0000000000..a66f7bd144 --- /dev/null +++ b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js @@ -0,0 +1,148 @@ +// +// highlightNearbyEntities.js +// +// Created by Dante Ruiz 2018-4-10 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + + +/* global Script, Controller, RIGHT_HAND, LEFT_HAND, MyAvatar, getGrabPointSphereOffset, + makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, + PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, + DEFAULT_SEARCH_SPHERE_DISTANCE, getGrabbableData, makeLaserParams, entityIsCloneable, Messages, print +*/ + +"use strict"; + +(function () { + Script.include("/~/system/libraries/controllerDispatcherUtils.js"); + Script.include("/~/system/libraries/controllers.js"); + Script.include("/~/system/libraries/cloneEntityUtils.js"); + var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); + + function differenceInArrays(firstArray, secondArray) { + var differenceArray = firstArray.filter(function(element) { + return secondArray.indexOf(element) < 0; + }); + + return differenceArray; + } + + function HighlightNearbyEntities(hand) { + this.hand = hand; + this.otherHand = hand === dispatcherUtils.RIGHT_HAND ? dispatcherUtils.LEFT_HAND : + dispatcherUtils.RIGHT_HAND; + this.highlightedEntities = []; + + this.parameters = dispatcherUtils.makeDispatcherModuleParameters( + 480, + this.hand === dispatcherUtils.RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + + this.isGrabable = function(controllerData, props) { + var canGrabEntity = false; + if (dispatcherUtils.entityIsGrabbable(props) || entityIsCloneable(props)) { + // if we've attempted to grab a child, roll up to the root of the tree + var groupRootProps = dispatcherUtils.findGroupParent(controllerData, props); + canGrabEntity = true; + if (!dispatcherUtils.entityIsGrabbable(groupRootProps)) { + canGrabEntity = false; + } + } + return canGrabEntity; + }; + + this.hasHyperLink = function(props) { + return (props.href !== "" && props.href !== undefined); + }; + + this.removeEntityFromHighlightList = function(entityID) { + var index = this.highlightedEntities.indexOf(entityID); + if (index > -1) { + this.highlightedEntities.splice(index, 1); + } + }; + + this.getOtherModule = function() { + var otherModule = this.hand === dispatcherUtils.RIGHT_HAND ? leftHighlightNearbyEntities : + rightHighlightNearbyEntities; + return otherModule; + }; + + this.getOtherHandHighlightedEntities = function() { + return this.getOtherModule().highlightedEntities; + }; + + this.highlightEntities = function(controllerData) { + var nearbyEntitiesProperties = controllerData.nearbyEntityProperties[this.hand]; + var otherHandHighlightedEntities = this.getOtherHandHighlightedEntities(); + var newHighlightedEntities = []; + var sensorScaleFactor = MyAvatar.sensorToWorldScale; + for (var i = 0; i < nearbyEntitiesProperties.length; i++) { + var props = nearbyEntitiesProperties[i]; + if (props.distance > dispatcherUtils.NEAR_GRAB_RADIUS * sensorScaleFactor) { + continue; + } + if (this.isGrabable(controllerData, props) || this.hasHyperLink(props)) { + dispatcherUtils.highlightTargetEntity(props.id); + newHighlightedEntities.push(props.id); + } + } + + var unhighlightEntities = differenceInArrays(this.highlightedEntities, newHighlightedEntities); + + unhighlightEntities.forEach(function(entityID) { + if (otherHandHighlightedEntities.indexOf(entityID) < 0 ) { + dispatcherUtils.unhighlightTargetEntity(entityID); + } + }); + this.highlightedEntities = newHighlightedEntities; + }; + + this.isReady = function(controllerData) { + this.highlightEntities(controllerData); + return dispatcherUtils.makeRunningValues(false, [], []); + }; + + this.run = function(controllerData) { + return this.isReady(controllerData); + }; + } + + var handleMessage = function(channel, message, sender) { + var data; + if (sender === MyAvatar.sessionUUID) { + if (channel === 'Hifi-unhighlight-entity') { + try { + data = JSON.parse(message); + + var hand = data.hand; + if (hand === dispatcherUtils.LEFT_HAND) { + leftHighlightNearbyEntities.removeEntityFromHighlightList(data.entityID); + } else if (hand === dispatcherUtils.RIGHT_HAND) { + rightHighlightNearbyEntities.removeEntityFromHighlightList(data.entityID); + } + } catch (e) { + print("Failed to parse message"); + } + } + } + }; + var leftHighlightNearbyEntities = new HighlightNearbyEntities(dispatcherUtils.LEFT_HAND); + var rightHighlightNearbyEntities = new HighlightNearbyEntities(dispatcherUtils.RIGHT_HAND); + + dispatcherUtils.enableDispatcherModule("LeftHighlightNearbyEntities", leftHighlightNearbyEntities); + dispatcherUtils.enableDispatcherModule("RightHighlightNearbyEntities", rightHighlightNearbyEntities); + + function cleanup() { + dispatcherUtils.disableDispatcherModule("LeftHighlightNearbyEntities"); + dispatcherUtils.disableDispatcherModule("RightHighlightNearbyEntities"); + } + Messages.subscribe('Hifi-unhighlight-entity'); + Messages.messageReceived.connect(handleMessage); + Script.scriptEnding.connect(cleanup); +}()); diff --git a/scripts/system/controllers/controllerModules/mouseHighlightEntities.js b/scripts/system/controllers/controllerModules/mouseHighlightEntities.js new file mode 100644 index 0000000000..6af1055a69 --- /dev/null +++ b/scripts/system/controllers/controllerModules/mouseHighlightEntities.js @@ -0,0 +1,77 @@ +// +// mouseHighlightEntities.js +// +// scripts/system/controllers/controllerModules/ +// +// Created by Dante Ruiz 2018-4-11 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* jslint bitwise: true */ + +/* global Script, print, Entities, Picks, HMD */ + + +(function() { + var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); + + function MouseHighlightEntities() { + this.highlightedEntity = null; + + this.parameters = dispatcherUtils.makeDispatcherModuleParameters( + 5, + ["mouse"], + [], + 100); + + this.isReady = function(controllerData) { + if (HMD.active) { + if (this.highlightedEntity) { + dispatcherUtils.unhighlightTargetEntity(this.highlightedEntity); + this.highlightedEntity = null; + } + } else { + var pickResult = controllerData.mouseRayPick; + if (pickResult.type === Picks.INTERSECTED_ENTITY) { + var targetEntityID = pickResult.objectID; + + if (this.highlightedEntity !== targetEntityID) { + var targetProps = Entities.getEntityProperties(targetEntityID, [ + "dynamic", "shapeType", "position", + "rotation", "dimensions", "density", + "userData", "locked", "type", "href" + ]); + + if (this.highlightedEntity) { + dispatcherUtils.unhighlightTargetEntity(this.highlightedEntity); + this.highlightedEntity = null; + } + + if (dispatcherUtils.entityIsGrabbable(targetProps)) { + // highlight entity + dispatcherUtils.highlightTargetEntity(targetEntityID); + this.highlightedEntity = targetEntityID; + } + } + } + } + + return dispatcherUtils.makeRunningValues(false, [], []); + }; + + this.run = function(controllerData) { + return this.isReady(controllerData); + }; + } + + var mouseHighlightEntities = new MouseHighlightEntities(); + dispatcherUtils.enableDispatcherModule("MouseHighlightEntities", mouseHighlightEntities); + + function cleanup() { + dispatcherUtils.disableDispatcherModule("MouseHighlightEntities"); + } + Script.scriptEnding.connect(cleanup); +})(); diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 147d6b807f..a4e439fe2f 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -10,7 +10,7 @@ propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity, - HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE + HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -114,6 +114,13 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "startNearGrab", args); + unhighlightTargetEntity(this.targetEntityID); + var message = { + hand: this.hand, + entityID: this.targetEntityID + }; + + Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message)); }; // this is for when the action is going to time-out diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 01c8424e0c..d454d20a02 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -11,7 +11,8 @@ TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, entityIsGrabbable, makeRunningValues, NEAR_GRAB_RADIUS, findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME, - TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox + TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Selection, DISPATCHER_HOVERING_LIST, Uuid, + highlightTargetEntity, unhighlightTargetEntity */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -34,6 +35,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.autoUnequipCounter = 0; this.lastUnexpectedChildrenCheckTime = 0; this.robbed = false; + this.highlightedEntity = null; this.parameters = makeDispatcherModuleParameters( 500, @@ -87,7 +89,13 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); this.startNearParentingGrabEntity = function (controllerData, targetProps) { Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); + unhighlightTargetEntity(this.targetEntityID); + var message = { + hand: this.hand, + entityID: this.targetEntityID + }; + Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message)); var handJointIndex; // if (this.ignoreIK) { // handJointIndex = this.controllerJointIndex; @@ -158,6 +166,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); grabbedEntity: this.targetEntityID, joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" })); + unhighlightTargetEntity(this.targetEntityID); this.grabbing = false; this.targetEntityID = null; this.robbed = false; @@ -280,6 +289,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); return makeRunningValues(false, [], []); // let nearActionGrabEntity handle it } else { this.targetEntityID = targetProps.id; + this.highlightedEntity = this.targetEntityID; + highlightTargetEntity(this.targetEntityID); return makeRunningValues(true, [this.targetEntityID], []); } } else { @@ -300,6 +311,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var props = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; if (!props) { // entity was deleted + unhighlightTargetEntity(this.targetEntityID); this.grabbing = false; this.targetEntityID = null; this.hapticTargetID = null; @@ -321,6 +333,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var readiness = this.isReady(controllerData); if (!readiness.active) { this.robbed = false; + unhighlightTargetEntity(this.highlightedEntity); return readiness; } if (controllerData.triggerClicks[this.hand] || controllerData.secondaryValues[this.hand] > BUMPER_ON_VALUE) { diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index 42db3d6f61..6a9cd9fbcd 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -7,7 +7,7 @@ /* global Script, Entities, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, getGrabbableData, - Vec3, TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS + Vec3, TRIGGER_OFF_VALUE, makeDispatcherModuleParameters, makeRunningValues, NEAR_GRAB_RADIUS, unhighlightTargetEntity */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -55,6 +55,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.startNearTrigger = function (controllerData) { var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "startNearTrigger", args); + unhighlightTargetEntity(this.targetEntityID); }; this.continueNearTrigger = function (controllerData) { diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 8db8e29f37..50e627fe5d 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -32,7 +32,9 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/hudOverlayPointer.js", "controllerModules/mouseHMD.js", "controllerModules/scaleEntity.js", - "controllerModules/nearGrabHyperLinkEntity.js" + "controllerModules/highlightNearbyEntities.js", + "controllerModules/nearGrabHyperLinkEntity.js", + "controllerModules/mouseHighlightEntities.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 0f8cc72e64..8ae94a4caa 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -15,7 +15,7 @@ // /* global MyAvatar, Entities, Script, Camera, Vec3, Reticle, Overlays, getEntityCustomData, Messages, Quat, Controller, - isInEditMode, HMD entityIsGrabbable, Picks, PickType, Pointers*/ + isInEditMode, HMD entityIsGrabbable, Picks, PickType, Pointers, unhighlightTargetEntity*/ (function() { // BEGIN LOCAL_SCOPE @@ -354,6 +354,7 @@ Grabber.prototype.pressEvent = function(event) { Pointers.setRenderState(this.mouseRayEntities, "grabbed"); Pointers.setLockEndUUID(this.mouseRayEntities, pickResults.objectID, false); + unhighlightTargetEntity(pickResults.objectID); mouse.startDrag(event); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 467842c712..6da3592999 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -452,6 +452,25 @@ var toolBar = (function () { } } + // Handles any edit mode updates required when domains have switched + function checkEditPermissionsAndUpdate() { + if ((createButton === null) || (createButton === undefined)) { + //--EARLY EXIT--( nothing to safely update ) + return; + } + + var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified()); + createButton.editProperties({ + icon: (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON), + captionColor: (hasRezPermissions ? "#ffffff" : "#888888"), + }); + + if (!hasRezPermissions && isActive) { + that.setActive(false); + tablet.gotoHomeScreen(); + } + } + function initialize() { Script.scriptEnding.connect(cleanup); Window.domainChanged.connect(function () { @@ -460,14 +479,21 @@ var toolBar = (function () { } that.setActive(false); that.clearEntityList(); + checkEditPermissionsAndUpdate(); }); Entities.canAdjustLocksChanged.connect(function (canAdjustLocks) { if (isActive && !canAdjustLocks) { that.setActive(false); } + checkEditPermissionsAndUpdate(); }); + Entities.canRezChanged.connect(checkEditPermissionsAndUpdate); + Entities.canRezTmpChanged.connect(checkEditPermissionsAndUpdate); + Entities.canRezCertifiedChanged.connect(checkEditPermissionsAndUpdate); + Entities.canRezTmpCertifiedChanged.connect(checkEditPermissionsAndUpdate); + var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified()); var createButtonIconRsrc = (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON); tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); @@ -853,37 +879,18 @@ function handleOverlaySelectionToolUpdates(channel, message, sender) { } } -// Handles any edit mode updates required when domains have switched -function handleDomainChange() { - if ( (createButton === null) || (createButton === undefined) ){ - //--EARLY EXIT--( nothing to safely update ) - return; - } - - var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified()); - createButton.editProperties({ - icon: (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON), - captionColor: (hasRezPermissions ? "#ffffff" : "#888888"), - }); -} - function handleMessagesReceived(channel, message, sender) { switch( channel ){ case 'entityToolUpdates': { handleOverlaySelectionToolUpdates( channel, message, sender ); break; } - case 'Toolbar-DomainChanged': { - handleDomainChange(); - break; - } default: { return; } } } -Messages.subscribe('Toolbar-DomainChanged'); Messages.subscribe("entityToolUpdates"); Messages.messageReceived.connect(handleMessagesReceived); @@ -1317,8 +1324,6 @@ Script.scriptEnding.connect(function () { Messages.messageReceived.disconnect(handleMessagesReceived); Messages.unsubscribe("entityToolUpdates"); - // Messages.unsubscribe("Toolbar-DomainChanged"); // Do not unsubscribe because the shapes.js app also subscribes and - // Messages.subscribe works script engine-wide which would mess things up if they're both run in the same engine. createButton = null; }); @@ -2334,6 +2339,11 @@ var PopupMenu = function () { Controller.mousePressEvent.disconnect(self.mousePressEvent); Controller.mouseMoveEvent.disconnect(self.mouseMoveEvent); Controller.mouseReleaseEvent.disconnect(self.mouseReleaseEvent); + + Entities.canRezChanged.disconnect(checkEditPermissionsAndUpdate); + Entities.canRezTmpChanged.disconnect(checkEditPermissionsAndUpdate); + Entities.canRezCertifiedChanged.disconnect(checkEditPermissionsAndUpdate); + Entities.canRezTmpCertifiedChanged.disconnect(checkEditPermissionsAndUpdate); } Controller.mousePressEvent.connect(self.mousePressEvent); diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 5ea1dd0963..0c3e6199f3 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -677,7 +677,6 @@ window.onload = function () { shareForUrl("p1"); appendShareBar("p1", messageOptions.isLoggedIn, messageOptions.canShare, true, false, false, messageOptions.canBlast); document.getElementById("p1").classList.remove("processingGif"); - document.getElementById("snap-button").disabled = false; } } else { imageCount = message.image_data.length; @@ -685,6 +684,7 @@ window.onload = function () { addImage(element, messageOptions.isLoggedIn, messageOptions.canShare, false, false, false, false, true); }); } + document.getElementById("snap-button").disabled = false; break; case 'captureSettings': handleCaptureSetting(message.setting); @@ -728,9 +728,7 @@ function takeSnapshot() { type: "snapshot", action: "takeSnapshot" })); - if (document.getElementById('stillAndGif').checked === true) { - document.getElementById("snap-button").disabled = true; - } + document.getElementById("snap-button").disabled = true; } function isPrintDisabled() { diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 61b54ca156..de5fad2ff9 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -7,6 +7,7 @@ /* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, + Selection, MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true, HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true, DEFAULT_REGISTRATION_POINT:true, INCHES_TO_METERS:true, @@ -22,6 +23,8 @@ DISPATCHER_PROPERTIES:true, HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, + DISPATCHER_HOVERING_LIST:true, + DISPATCHER_HOVERING_STYLE:true, Entities, makeDispatcherModuleParameters:true, makeRunningValues:true, @@ -49,7 +52,10 @@ TEAR_AWAY_DISTANCE:true, TEAR_AWAY_COUNT:true, TEAR_AWAY_CHECK_TIME:true, - distanceBetweenPointAndEntityBoundingBox:true + distanceBetweenPointAndEntityBoundingBox:true, + highlightTargetEntity:true, + clearHighlightedEntities:true, + unhighlightTargetEntity:true */ MSECS_PER_SEC = 1000.0; @@ -88,6 +94,19 @@ NEAR_GRAB_RADIUS = 1.0; TEAR_AWAY_DISTANCE = 0.1; // ungrab an entity if its bounding-box moves this far from the hand TEAR_AWAY_COUNT = 2; // multiply by TEAR_AWAY_CHECK_TIME to know how long the item must be away TEAR_AWAY_CHECK_TIME = 0.15; // seconds, duration between checks +DISPATCHER_HOVERING_LIST = "dispactherHoveringList"; +DISPATCHER_HOVERING_STYLE = { + isOutlineSmooth: true, + outlineWidth: 0, + outlineUnoccludedColor: {red: 255, green: 128, blue: 128}, + outlineUnoccludedAlpha: 0.0, + outlineOccludedColor: {red: 255, green: 128, blue: 128}, + outlineOccludedAlpha:0.0, + fillUnoccludedColor: {red: 255, green: 255, blue: 255}, + fillUnoccludedAlpha: 0.12, + fillOccludedColor: {red: 255, green: 255, blue: 255}, + fillOccludedAlpha: 0.0 +}; DISPATCHER_PROPERTIES = [ "position", @@ -220,6 +239,18 @@ entityIsGrabbable = function (props) { return true; }; +clearHighlightedEntities = function() { + Selection.clearSelectedItemsList(DISPATCHER_HOVERING_LIST); +}; + +highlightTargetEntity = function(entityID) { + Selection.addToSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", entityID); +}; + +unhighlightTargetEntity = function(entityID) { + Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", entityID); +}; + entityIsDistanceGrabbable = function(props) { if (!entityIsGrabbable(props)) { return false; @@ -397,7 +428,11 @@ if (typeof module !== 'undefined') { makeDispatcherModuleParameters: makeDispatcherModuleParameters, enableDispatcherModule: enableDispatcherModule, disableDispatcherModule: disableDispatcherModule, + highlightTargetEntity: highlightTargetEntity, + unhighlightTargetEntity: unhighlightTargetEntity, + clearHighlightedEntities: clearHighlightedEntities, makeRunningValues: makeRunningValues, + findGroupParent: findGroupParent, LEFT_HAND: LEFT_HAND, RIGHT_HAND: RIGHT_HAND, BUMPER_ON_VALUE: BUMPER_ON_VALUE, @@ -408,6 +443,7 @@ if (typeof module !== 'undefined') { projectOntoOverlayXYPlane: projectOntoOverlayXYPlane, projectOntoEntityXYPlane: projectOntoEntityXYPlane, TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE, - TRIGGER_ON_VALUE: TRIGGER_ON_VALUE + TRIGGER_ON_VALUE: TRIGGER_ON_VALUE, + DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST }; } diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index b284cab345..2322210522 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -13,9 +13,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global HIFI_PUBLIC_BUCKET, SPACE_LOCAL, Script, SelectionManager */ - -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +/* global SPACE_LOCAL, SelectionManager */ SPACE_LOCAL = "local"; SPACE_WORLD = "world"; @@ -50,6 +48,7 @@ SelectionManager = (function() { messageParsed = JSON.parse(message); } catch (err) { print("ERROR: entitySelectionTool.handleEntitySelectionToolUpdates - got malformed message: " + message); + return; } if (messageParsed.method === "selectEntity") { @@ -155,6 +154,20 @@ SelectionManager = (function() { that._update(true); }; + that.duplicateSelection = function() { + var duplicatedEntityIDs = []; + Object.keys(that.savedProperties).forEach(function(otherEntityID) { + var properties = that.savedProperties[otherEntityID]; + if (!properties.locked && (!properties.clientOnly || properties.owningAvatarID === MyAvatar.sessionUUID)) { + duplicatedEntityIDs.push({ + entityID: Entities.addEntity(properties), + properties: properties + }); + } + }); + return duplicatedEntityIDs; + } + that._update = function(selectionUpdated) { var properties = null; if (that.selections.length === 0) { @@ -443,7 +456,7 @@ SelectionDisplay = (function() { solid: true, visible: false, ignoreRayIntersection: true, - drawInFront: true, + drawInFront: true } var handleStretchXPanel = Overlays.addOverlay("shape", handlePropertiesStretchPanel); Overlays.editOverlay(handleStretchXPanel, { color : COLOR_RED }); @@ -749,6 +762,7 @@ SelectionDisplay = (function() { } else if (overlay === handleTranslateZCylinder) { return handleTranslateZCone; } + return Uuid.NULL; }; // FUNCTION: MOUSE MOVE EVENT @@ -1038,13 +1052,13 @@ SelectionDisplay = (function() { var toCameraDistance = getDistanceToCamera(position); var localRotationX = Quat.fromPitchYawRollDegrees(0, 0, -90); - rotationX = Quat.multiply(rotation, localRotationX); + var rotationX = Quat.multiply(rotation, localRotationX); worldRotationX = rotationX; var localRotationY = Quat.fromPitchYawRollDegrees(0, 90, 0); - rotationY = Quat.multiply(rotation, localRotationY); + var rotationY = Quat.multiply(rotation, localRotationY); worldRotationY = rotationY; var localRotationZ = Quat.fromPitchYawRollDegrees(90, 0, 0); - rotationZ = Quat.multiply(rotation, localRotationZ); + var rotationZ = Quat.multiply(rotation, localRotationZ); worldRotationZ = rotationZ; // in HMD we clamp the overlays to the bounding box for now so lasers can hit them @@ -1260,10 +1274,9 @@ SelectionDisplay = (function() { dimensions: stretchPanelXDimensions }); var stretchPanelYDimensions = Vec3.subtract(scaleLTNCubePositionRotated, scaleRTFCubePositionRotated); - var tempX = Math.abs(stretchPanelYDimensions.x); stretchPanelYDimensions.x = Math.abs(stretchPanelYDimensions.z); stretchPanelYDimensions.y = STRETCH_PANEL_WIDTH; - stretchPanelYDimensions.z = tempX; + stretchPanelYDimensions.z = Math.abs(stretchPanelYDimensions.x); var stretchPanelYPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:dimensions.y / 2, z:0 })); Overlays.editOverlay(handleStretchYPanel, { position: stretchPanelYPosition, @@ -1271,9 +1284,8 @@ SelectionDisplay = (function() { dimensions: stretchPanelYDimensions }); var stretchPanelZDimensions = Vec3.subtract(scaleLTNCubePositionRotated, scaleRBFCubePositionRotated); - var tempX = Math.abs(stretchPanelZDimensions.x); stretchPanelZDimensions.x = Math.abs(stretchPanelZDimensions.y); - stretchPanelZDimensions.y = tempX; + stretchPanelZDimensions.y = Math.abs(stretchPanelZDimensions.x); stretchPanelZDimensions.z = STRETCH_PANEL_WIDTH; var stretchPanelZPosition = Vec3.sum(position, Vec3.multiplyQbyV(rotation, { x:0, y:0, z:dimensions.z / 2 })); Overlays.editOverlay(handleStretchZPanel, { @@ -1519,17 +1531,7 @@ SelectionDisplay = (function() { // copy of the selected entities and move the _original_ entities, not // the new ones. if (event.isAlt || doClone) { - duplicatedEntityIDs = []; - for (var otherEntityID in SelectionManager.savedProperties) { - var properties = SelectionManager.savedProperties[otherEntityID]; - if (!properties.locked) { - var entityID = Entities.addEntity(properties); - duplicatedEntityIDs.push({ - entityID: entityID, - properties: properties - }); - } - } + duplicatedEntityIDs = SelectionManager.duplicateSelection(); } else { duplicatedEntityIDs = null; } @@ -1690,17 +1692,7 @@ SelectionDisplay = (function() { // copy of the selected entities and move the _original_ entities, not // the new ones. if (event.isAlt) { - duplicatedEntityIDs = []; - for (var otherEntityID in SelectionManager.savedProperties) { - var properties = SelectionManager.savedProperties[otherEntityID]; - if (!properties.locked) { - var entityID = Entities.addEntity(properties); - duplicatedEntityIDs.push({ - entityID: entityID, - properties: properties - }); - } - } + duplicatedEntityIDs = SelectionManager.duplicateSelection(); } else { duplicatedEntityIDs = null; } diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 658d1c3ced..5690c91c76 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -275,7 +275,7 @@ function onMessage(message) { } } -var POLAROID_PRINT_SOUND = SoundCache.getSound(Script.resolvePath("assets/sounds/sound-print-photo.wav")); +var POLAROID_PRINT_SOUND = SoundCache.getSound(Script.resourcesPath() + "sounds/snapshot/sound-print-photo.wav"); var POLAROID_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/alan/dev/Test/snapshot.fbx'; function printToPolaroid(image_url) { @@ -347,7 +347,7 @@ function fillImageDataFromPrevious() { story_id: previousStillSnapStoryID, blastButtonDisabled: previousStillSnapBlastingDisabled, hifiButtonDisabled: previousStillSnapHifiSharingDisabled, - errorPath: Script.resolvePath(Script.resourcesPath() + 'snapshot/img/no-image.jpg') + errorPath: Script.resourcesPath() + 'snapshot/img/no-image.jpg' }); } if (previousAnimatedSnapPath !== "") { @@ -356,7 +356,7 @@ function fillImageDataFromPrevious() { story_id: previousAnimatedSnapStoryID, blastButtonDisabled: previousAnimatedSnapBlastingDisabled, hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled, - errorPath: Script.resolvePath(Script.resourcesPath() + 'snapshot/img/no-image.jpg') + errorPath: Script.resourcesPath() + 'snapshot/img/no-image.jpg' }); } } @@ -473,7 +473,7 @@ function takeSnapshot() { Menu.setIsOptionChecked("Overlays", false); } - var snapActivateSound = SoundCache.getSound(Script.resolvePath("../../resources/sounds/snap.wav")); + var snapActivateSound = SoundCache.getSound(Script.resourcesPath() + "sounds/snapshot/snap.wav"); // take snapshot (with no notification) Script.setTimeout(function () { @@ -596,7 +596,7 @@ function processingGifStarted(pathStillSnapshot) { snapshotOptions = { containsGif: true, processingGif: true, - loadingGifPath: Script.resolvePath(Script.resourcesPath() + 'icons/loadingDark.gif'), + loadingGifPath: Script.resourcesPath() + 'icons/loadingDark.gif', canShare: canShare, isLoggedIn: isLoggedIn }; diff --git a/tools/hfudt.lua b/tools/hfudt.lua index 97fade9c0e..f45315a3de 100644 --- a/tools/hfudt.lua +++ b/tools/hfudt.lua @@ -19,6 +19,7 @@ local f_message_part_number = ProtoField.uint32("hfudt.message_part_number", "Me local f_type = ProtoField.uint8("hfudt.type", "Type", base.DEC) local f_version = ProtoField.uint8("hfudt.version", "Version", base.DEC) local f_type_text = ProtoField.string("hfudt.type_text", "TypeText") +local f_sender_id = ProtoField.guid("hfudt.sender_id", "Sender ID", base.DEC) -- create the fields for control packets in HFUDT local f_control_type = ProtoField.uint16("hfudt.control_type", "Control Type", base.DEC) @@ -28,11 +29,29 @@ local f_control_sub_sequence = ProtoField.uint32("hfudt.control_sub_sequence", " local f_nak_sequence_number = ProtoField.uint32("hfudt.nak_sequence_number", "NAKed Sequence Number", base.DEC) local f_nak_range_end = ProtoField.uint32("hfudt.nak_range_end", "NAK Range End", base.DEC) +-- avatar data fields +local f_avatar_data_id = ProtoField.guid("hfudt.avatar_id", "Avatar ID", base.DEC) +local f_avatar_data_has_flags = ProtoField.string("hfudt.avatar_has_flags", "Has Flags") +local f_avatar_data_position = ProtoField.string("hfudt.avatar_data_position", "Position") +local f_avatar_data_dimensions = ProtoField.string("hfudt.avatar_data_dimensions", "Dimensions") +local f_avatar_data_offset = ProtoField.string("hfudt.avatar_data_offset", "Offset") +local f_avatar_data_look_at_position = ProtoField.string("hfudt.avatar_data_look_at_position", "Look At Position") +local f_avatar_data_audio_loudness = ProtoField.string("hfudt.avatar_data_audio_loudness", "Audio Loudness") +local f_avatar_data_additional_flags = ProtoField.string("hfudt.avatar_data_additional_flags", "Additional Flags") +local f_avatar_data_parent_id = ProtoField.guid("hfudt.avatar_data_parent_id", "Parent ID") +local f_avatar_data_parent_joint_index = ProtoField.string("hfudt.avatar_data_parent_joint_index", "Parent Joint Index") +local f_avatar_data_local_position = ProtoField.string("hfudt.avatar_data_local_position", "Local Position") +local f_avatar_data_valid_rotations = ProtoField.string("hfudt.avatar_data_valid_rotations", "Valid Rotations") +local f_avatar_data_valid_translations = ProtoField.string("hfudt.avatar_data_valid_translations", "Valid Translations") +local f_avatar_data_default_rotations = ProtoField.string("hfudt.avatar_data_default_rotations", "Valid Default") +local f_avatar_data_default_translations = ProtoField.string("hfudt.avatar_data_default_translations", "Valid Default") + local SEQUENCE_NUMBER_MASK = 0x07FFFFFF p_hfudt.fields = { f_length, f_control_bit, f_reliable_bit, f_message_bit, f_sequence_number, f_type, f_type_text, f_version, + f_sender_id, f_avatar_data_id, f_avatar_data_parent_id, f_message_position, f_message_number, f_message_part_number, f_obfuscation_level, f_control_type, f_control_type_text, f_control_sub_sequence, f_ack_sequence_number, f_nak_sequence_number, f_nak_range_end, f_data @@ -261,16 +280,287 @@ function p_hfudt.dissector (buf, pinfo, root) -- read the version subtree:add(f_version, buf(payload_offset + 1, 1):le_uint()) - data_length = buf:len() - (payload_offset + 2) + -- read node GUID + local sender_id = buf(payload_offset + 2, 16) + subtree:add(f_sender_id, sender_id) - -- pull the data that is the rest of the packet - subtree:add(f_data, buf(payload_offset + 2, data_length)) + local i = payload_offset + 18 + + -- skip MD6 checksum + 16 bytes + i = i + 16 + + -- AvatarData or BulkAvatarDataPacket + if packet_type == 6 or packet_type == 11 then + + local avatar_data + if packet_type == 6 then + -- AvatarData packet + + -- avatar_id is same as sender_id + subtree:add(f_avatar_data_id, sender_id) + + -- uint16 sequence_number + local sequence_number = buf(i, 2):le_uint() + i = i + 2 + + local avatar_data_packet_len = buf:len() - i + avatar_data = decode_avatar_data_packet(buf(i, avatar_data_packet_len)) + i = i + avatar_data_packet_len + + add_avatar_data_subtrees(avatar_data) + + else + -- BulkAvatarData packet + while i < buf:len() do + -- avatar_id is first 16 bytes + subtree:add(f_avatar_data_id, buf(i, 16)) + i = i + 16 + + local avatar_data_packet_len = buf:len() - i + avatar_data = decode_avatar_data_packet(buf(i, avatar_data_packet_len)) + i = i + avatar_data_packet_len + + add_avatar_data_subtrees(avatar_data) + end + end + end end -- return the size of the header return buf:len() - end +end + +function add_avatar_data_subtrees(avatar_data) + if avatar_data["has_flags"] then + subtree:add(f_avatar_data_has_flags, avatar_data["has_flags"]) + end + if avatar_data["position"] then + subtree:add(f_avatar_data_position, avatar_data["position"]) + end + if avatar_data["dimensions"] then + subtree:add(f_avatar_data_dimensions, avatar_data["dimensions"]) + end + if avatar_data["offset"] then + subtree:add(f_avatar_data_offset, avatar_data["offset"]) + end + if avatar_data["look_at_position"] then + subtree:add(f_avatar_data_look_at_position, avatar_data["look_at_position"]) + end + if avatar_data["audio_loudness"] then + subtree:add(f_avatar_data_audio_loudness, avatar_data["audio_loudness"]) + end + if avatar_data["additional_flags"] then + subtree:add(f_avatar_data_additional_flags, avatar_data["additional_flags"]) + end + if avatar_data["parent_id"] then + subtree:add(f_avatar_data_parent_id, avatar_data["parent_id"]) + end + if avatar_data["parent_joint_index"] then + subtree:add(f_avatar_data_parent_joint_index, avatar_data["parent_joint_index"]) + end + if avatar_data["local_position"] then + subtree:add(f_avatar_data_local_position, avatar_data["local_position"]) + end + if avatar_data["valid_rotations"] then + subtree:add(f_avatar_data_valid_rotations, avatar_data["valid_rotations"]) + end + if avatar_data["valid_translations"] then + subtree:add(f_avatar_data_valid_translations, avatar_data["valid_translations"]) + end + if avatar_data["default_rotations"] then + subtree:add(f_avatar_data_default_rotations, avatar_data["default_rotations"]) + end + if avatar_data["default_translations"] then + subtree:add(f_avatar_data_default_translations, avatar_data["default_translations"]) + end +end + +function decode_vec3(buf) + local i = 0 + local x = buf(i, 4):le_float() + i = i + 4 + local y = buf(i, 4):le_float() + i = i + 4 + local z = buf(i, 4):le_float() + i = i + 4 + return {x, y, z} +end + +function decode_validity_bits(buf, num_bits) + -- first pass, decode each bit into an array of booleans + local i = 0 + local bit = 0 + local booleans = {} + for n = 1, num_bits do + local value = (bit32.band(buf(i, 1):uint(), bit32.lshift(1, bit)) ~= 0) + booleans[#booleans + 1] = value + bit = bit + 1 + if bit == 8 then + i = i + 1 + bit = 0 + end + end + + -- second pass, create a list of indices whos booleans are true + local result = {} + for n = 1, #booleans do + if booleans[n] then + result[#result + 1] = n + end + end + + return result +end + +function decode_avatar_data_packet(buf) + + local i = 0 + local result = {} + + -- uint16 has_flags + local has_flags = buf(i, 2):le_uint() + i = i + 2 + + local has_global_position = (bit32.band(has_flags, 1) ~= 0) + local has_bounding_box = (bit32.band(has_flags, 2) ~= 0) + local has_orientation = (bit32.band(has_flags, 4) ~= 0) + local has_scale = (bit32.band(has_flags, 8) ~= 0) + local has_look_at_position = (bit32.band(has_flags, 16) ~= 0) + local has_audio_loudness = (bit32.band(has_flags, 32) ~= 0) + local has_sensor_to_world_matrix = (bit32.band(has_flags, 64) ~= 0) + local has_additional_flags = (bit32.band(has_flags, 128) ~= 0) + local has_parent_info = (bit32.band(has_flags, 256) ~= 0) + local has_local_position = (bit32.band(has_flags, 512) ~= 0) + local has_face_tracker_info = (bit32.band(has_flags, 1024) ~= 0) + local has_joint_data = (bit32.band(has_flags, 2048) ~= 0) + local has_joint_default_pose_flags = (bit32.band(has_flags, 4096) ~= 0) + + result["has_flags"] = string.format("HasFlags: 0x%x", has_flags) + + if has_global_position then + local position = decode_vec3(buf(i, 12)) + result["position"] = string.format("Position: %.3f, %.3f, %.3f", position[1], position[2], position[3]) + i = i + 12 + end + + if has_bounding_box then + local dimensions = decode_vec3(buf(i, 12)) + i = i + 12 + local offset = decode_vec3(buf(i, 12)) + i = i + 12 + result["dimensions"] = string.format("Dimensions: %.3f, %.3f, %.3f", dimensions[1], dimensions[2], dimensions[3]) + result["offset"] = string.format("Offset: %.3f, %.3f, %.3f", offset[1], offset[2], offset[3]) + end + + if has_orientation then + -- TODO: orientation is hard to decode... + i = i + 6 + end + + if has_scale then + -- TODO: scale is hard to decode... + i = i + 2 + end + + if has_look_at_position then + local look_at = decode_vec3(buf(i, 12)) + i = i + 12 + result["look_at_position"] = string.format("Look At Position: %.3f, %.3f, %.3f", look_at[1], look_at[2], look_at[3]) + end + + if has_audio_loudness then + local loudness = buf(i, 1):uint() + i = i + 1 + result["audio_loudness"] = string.format("Audio Loudness: %d", loudness) + end + + if has_sensor_to_world_matrix then + -- TODO: sensor to world matrix is hard to decode + i = i + 20 + end + + if has_additional_flags then + local flags = buf(i, 1):uint() + i = i + 1 + result["additional_flags"] = string.format("Additional Flags: 0x%x", flags) + end + + if has_parent_info then + local parent_id = buf(i, 16) + i = i + 16 + local parent_joint_index = buf(i, 2):le_int() + i = i + 2 + result["parent_id"] = parent_id + result["parent_joint_index"] = string.format("Parent Joint Index: %d", parent_joint_index) + end + + if has_local_position then + local local_pos = decode_vec3(buf(i, 12)) + i = i + 12 + result["local_position"] = string.format("Local Position: %.3f, %.3f, %.3f", local_pos[1], local_pos[2], local_pos[3]) + end + + if has_face_tracker_info then + local left_eye_blink = buf(i, 4):le_float() + i = i + 4 + local right_eye_blink = buf(i, 4):le_float() + i = i + 4 + local average_loudness = buf(i, 4):le_float() + i = i + 4 + local brow_audio_lift = buf(i, 4):le_float() + i = i + 4 + local num_blendshape_coefficients = buf(i, 1):uint() + i = i + 1 + local blendshape_coefficients = {} + for n = 1, num_blendshape_coefficients do + blendshape_coefficients[n] = buf(i, 4):le_float() + i = i + 4 + end + -- TODO: insert blendshapes into result + end + + if has_joint_data then + + local num_joints = buf(i, 1):uint() + i = i + 1 + local num_validity_bytes = math.ceil(num_joints / 8) + + local indices = decode_validity_bits(buf(i, num_validity_bytes), num_joints) + i = i + num_validity_bytes + result["valid_rotations"] = "Valid Rotations: " .. string.format("(%d/%d) {", #indices, num_joints) .. table.concat(indices, ", ") .. "}" + + -- TODO: skip rotations for now + i = i + #indices * 6 + + indices = decode_validity_bits(buf(i, num_validity_bytes), num_joints) + i = i + num_validity_bytes + result["valid_translations"] = "Valid Translations: " .. string.format("(%d/%d) {", #indices, num_joints) .. table.concat(indices, ", ") .. "}" + + -- TODO: skip translations for now + i = i + #indices * 6 + + -- TODO: skip hand controller data + i = i + 24 + + end + + if has_joint_default_pose_flags then + local num_joints = buf(i, 1):uint() + i = i + 1 + local num_validity_bytes = math.ceil(num_joints / 8) + + local indices = decode_validity_bits(buf(i, num_validity_bytes), num_joints) + i = i + num_validity_bytes + result["default_rotations"] = "Default Rotations: " .. string.format("(%d/%d) {", #indices, num_joints) .. table.concat(indices, ", ") .. "}" + + indices = decode_validity_bits(buf(i, num_validity_bytes), num_joints) + i = i + num_validity_bytes + result["default_translations"] = "Default Translations: " .. string.format("(%d/%d) {", #indices, num_joints) .. table.concat(indices, ", ") .. "}" + end + + return result +end function p_hfudt.init() local udp_dissector_table = DissectorTable.get("udp.port") diff --git a/tools/jsdoc/CMakeLists.txt b/tools/jsdoc/CMakeLists.txt index 292523a813..4a6c18f243 100644 --- a/tools/jsdoc/CMakeLists.txt +++ b/tools/jsdoc/CMakeLists.txt @@ -13,5 +13,5 @@ file(TO_NATIVE_PATH ${JSDOC_WORKING_DIR} NATIVE_JSDOC_WORKING_DIR) add_custom_command(TARGET ${TARGET_NAME} COMMAND ${NPM_EXECUTABLE} --no-progress install && ${JSDOC_PATH} ${NATIVE_JSDOC_WORKING_DIR} -c ${JSDOC_CONFIG_PATH} -d ${OUTPUT_DIR} WORKING_DIRECTORY ${JSDOC_WORKING_DIR} - COMMENT "generate the JSDoc JSON for the JSConsole auto-completer" + COMMENT "generate the JSDoc JSON" )