diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 1685f08e01..0486a97ede 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -60,8 +60,8 @@ public: virtual void trackViewerGone(const QUuid& sessionID) override; public slots: - virtual void nodeAdded(SharedNodePointer node); - virtual void nodeKilled(SharedNodePointer node); + virtual void nodeAdded(SharedNodePointer node) override; + virtual void nodeKilled(SharedNodePointer node) override; void pruneDeletedEntities(); protected: diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 919ac37ee9..61cc775e08 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -55,9 +55,9 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointergetSize() == 0) { return; } - + QDataStream packetStream(message->getMessage()); - + // read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it NodeConnectionData nodeConnection = NodeConnectionData::fromDataStream(packetStream, message->getSenderSockAddr()); @@ -105,6 +105,7 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer(node->getLinkedData()); nodeData->setSendingSockAddr(message->getSenderSockAddr()); nodeData->setNodeInterestSet(nodeConnection.interestList.toSet()); + nodeData->setPlaceName(nodeConnection.placeName); // signal that we just connected a node so the DomainServer can get it a list // and broadcast its presence right away @@ -150,6 +151,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID()); nodeData->setWalletUUID(it->second.getWalletUUID()); nodeData->setNodeVersion(it->second.getNodeVersion()); + nodeData->setWasAssigned(true); // cleanup the PendingAssignedNodeData for this assignment now that it's connecting _pendingAssignedNodes.erase(it); @@ -282,7 +284,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect // set the edit rights for this user newNode->setIsAllowedEditor(isAllowedEditor); newNode->setCanRez(canRez); - + // grab the linked data for our new node so we can set the username DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 0aab6b7e31..cfec72a24b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -462,7 +462,7 @@ void DomainServer::setupAutomaticNetworking() { nodeList->startSTUNPublicSocketUpdate(); } else { // send our heartbeat to data server so it knows what our network settings are - sendHeartbeatToDataServer(); + sendHeartbeatToMetaverse(); } } else { qDebug() << "Cannot enable domain-server automatic networking without a domain ID." @@ -471,7 +471,7 @@ void DomainServer::setupAutomaticNetworking() { return; } } else { - sendHeartbeatToDataServer(); + sendHeartbeatToMetaverse(); } qDebug() << "Updating automatic networking setting in domain-server to" << _automaticNetworkingSetting; @@ -480,7 +480,7 @@ void DomainServer::setupAutomaticNetworking() { const int DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS = 15 * 1000; QTimer* dataHeartbeatTimer = new QTimer(this); - connect(dataHeartbeatTimer, SIGNAL(timeout()), this, SLOT(sendHeartbeatToDataServer())); + connect(dataHeartbeatTimer, SIGNAL(timeout()), this, SLOT(sendHeartbeatToMetaverse())); dataHeartbeatTimer->start(DOMAIN_SERVER_DATA_WEB_HEARTBEAT_MSECS); } @@ -678,6 +678,9 @@ void DomainServer::processListRequestPacket(QSharedPointer mess DomainServerNodeData* nodeData = reinterpret_cast(sendingNode->getLinkedData()); nodeData->setNodeInterestSet(nodeRequestData.interestList.toSet()); + // update the connecting hostname in case it has changed + nodeData->setPlaceName(nodeRequestData.placeName); + sendDomainListToNode(sendingNode, message->getSenderSockAddr()); } @@ -1029,11 +1032,11 @@ QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) { const QString DOMAIN_UPDATE_AUTOMATIC_NETWORKING_KEY = "automatic_networking"; void DomainServer::performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr) { - sendHeartbeatToDataServer(newPublicSockAddr.getAddress().toString()); + sendHeartbeatToMetaverse(newPublicSockAddr.getAddress().toString()); } -void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) { +void DomainServer::sendHeartbeatToMetaverse(const QString& networkAddress) { const QString DOMAIN_UPDATE = "/api/v1/domains/%1"; auto nodeList = DependencyManager::get(); @@ -1056,20 +1059,34 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) { domainObject[RESTRICTED_ACCESS_FLAG] = _settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); - // add the number of currently connected agent users - int numConnectedAuthedUsers = 0; + // figure out the breakdown of currently connected interface clients + int numConnectedUnassigned = 0; + QJsonObject userHostnames; - nodeList->eachNode([&numConnectedAuthedUsers](const SharedNodePointer& node){ - if (node->getLinkedData() && !static_cast(node->getLinkedData())->getUsername().isEmpty()) { - ++numConnectedAuthedUsers; + static const QString DEFAULT_HOSTNAME = "*"; + + nodeList->eachNode([&numConnectedUnassigned, &userHostnames](const SharedNodePointer& node) { + if (node->getLinkedData()) { + auto nodeData = static_cast(node->getLinkedData()); + + if (!nodeData->wasAssigned()) { + ++numConnectedUnassigned; + + // increment the count for this hostname (or the default if we don't have one) + auto hostname = nodeData->getPlaceName().isEmpty() ? DEFAULT_HOSTNAME : nodeData->getPlaceName(); + userHostnames[hostname] = userHostnames[hostname].toInt() + 1; + } } }); - const QString DOMAIN_HEARTBEAT_KEY = "heartbeat"; - const QString HEARTBEAT_NUM_USERS_KEY = "num_users"; + static const QString DOMAIN_HEARTBEAT_KEY = "heartbeat"; + static const QString HEARTBEAT_NUM_USERS_KEY = "num_users"; + static const QString HEARTBEAT_USER_HOSTNAMES_KEY = "user_hostnames"; QJsonObject heartbeatObject; - heartbeatObject[HEARTBEAT_NUM_USERS_KEY] = numConnectedAuthedUsers; + heartbeatObject[HEARTBEAT_NUM_USERS_KEY] = numConnectedUnassigned; + heartbeatObject[HEARTBEAT_USER_HOSTNAMES_KEY] = userHostnames; + domainObject[DOMAIN_HEARTBEAT_KEY] = heartbeatObject; QString domainUpdateJSON = QString("{\"domain\": %1 }").arg(QString(QJsonDocument(domainObject).toJson())); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 93bb5de494..fef3221b7d 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -71,7 +71,7 @@ private slots: void sendPendingTransactionsToServer(); void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr); - void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); } + void sendHeartbeatToMetaverse() { sendHeartbeatToMetaverse(QString()); } void sendHeartbeatToIceServer(); void handleConnectedNode(SharedNodePointer newNode); @@ -103,7 +103,7 @@ private: void setupAutomaticNetworking(); void setupICEHeartbeatForFullNetworking(); - void sendHeartbeatToDataServer(const QString& networkAddress); + void sendHeartbeatToMetaverse(const QString& networkAddress); void randomizeICEServerAddress(bool shouldTriggerHostLookup); diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index cd68aa3006..f95403c779 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -56,6 +56,12 @@ public: void addOverrideForKey(const QString& key, const QString& value, const QString& overrideValue); void removeOverrideForKey(const QString& key, const QString& value); + + const QString& getPlaceName() { return _placeName; } + void setPlaceName(const QString& placeName) { _placeName = placeName; } + + bool wasAssigned() const { return _wasAssigned; }; + void setWasAssigned(bool wasAssigned) { _wasAssigned = wasAssigned; } private: QJsonObject overrideValuesIfNeeded(const QJsonObject& newStats); @@ -75,6 +81,10 @@ private: bool _isAuthenticated = true; NodeSet _nodeInterestSet; QString _nodeVersion; + + QString _placeName; + + bool _wasAssigned { false }; }; #endif // hifi_DomainServerNodeData_h diff --git a/domain-server/src/NodeConnectionData.cpp b/domain-server/src/NodeConnectionData.cpp index 80cb5950be..28f769298c 100644 --- a/domain-server/src/NodeConnectionData.cpp +++ b/domain-server/src/NodeConnectionData.cpp @@ -23,7 +23,7 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c dataStream >> newHeader.nodeType >> newHeader.publicSockAddr >> newHeader.localSockAddr - >> newHeader.interestList; + >> newHeader.interestList >> newHeader.placeName; newHeader.senderSockAddr = senderSockAddr; diff --git a/domain-server/src/NodeConnectionData.h b/domain-server/src/NodeConnectionData.h index 6b3b8eb7c1..34119ffdab 100644 --- a/domain-server/src/NodeConnectionData.h +++ b/domain-server/src/NodeConnectionData.h @@ -27,6 +27,7 @@ public: HifiSockAddr localSockAddr; HifiSockAddr senderSockAddr; QList interestList; + QString placeName; }; diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 4f58cf73db..4381f3321b 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -4,10 +4,6 @@ project(${TARGET_NAME}) # set a default root dir for each of our optional externals if it was not passed set(OPTIONAL_EXTERNALS "LeapMotion") -if(WIN32) - list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient") -endif() - foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) diff --git a/interface/resources/controllers/spacemouse.json b/interface/resources/controllers/spacemouse.json index adcfef71a6..1480e1957d 100644 --- a/interface/resources/controllers/spacemouse.json +++ b/interface/resources/controllers/spacemouse.json @@ -2,11 +2,11 @@ "name": "Spacemouse to Standard", "channels": [ - { "from": "Spacemouse.TranslateX", "to": "Standard.RX" }, + { "from": "Spacemouse.TranslateX", "to": "Standard.LX" }, { "from": "Spacemouse.TranslateY", "to": "Standard.LY" }, { "from": "Spacemouse.TranslateZ", "to": "Standard.RY" }, - { "from": "Spacemouse.RotateZ", "to": "Standard.LX" }, + { "from": "Spacemouse.RotateZ", "to": "Standard.RX" }, { "from": "Spacemouse.LeftButton", "to": "Standard.LB" }, { "from": "Spacemouse.RightButton", "to": "Standard.RB" } diff --git a/interface/resources/qml/hifi/MenuOption.qml b/interface/resources/qml/hifi/MenuOption.qml deleted file mode 100644 index 46cf5d9662..0000000000 --- a/interface/resources/qml/hifi/MenuOption.qml +++ /dev/null @@ -1,165 +0,0 @@ -import QtQuick 2.5 - -QtObject { - readonly property string aboutApp: "About Interface"; - readonly property string addRemoveFriends: "Add/Remove Friends..."; - readonly property string addressBar: "Show Address Bar"; - readonly property string animations: "Animations..."; - readonly property string animDebugDrawAnimPose: "Debug Draw Animation"; - readonly property string animDebugDrawDefaultPose: "Debug Draw Default Pose"; - readonly property string animDebugDrawPosition: "Debug Draw Position"; - readonly property string antialiasing: "Antialiasing"; - readonly property string assetMigration: "ATP Asset Migration"; - readonly property string assetServer: "Asset Server"; - readonly property string atmosphere: "Atmosphere"; - readonly property string attachments: "Attachments..."; - readonly property string audioNetworkStats: "Audio Network Stats"; - readonly property string audioNoiseReduction: "Audio Noise Reduction"; - readonly property string audioScope: "Show Scope"; - readonly property string audioScopeFiftyFrames: "Fifty"; - readonly property string audioScopeFiveFrames: "Five"; - readonly property string audioScopeFrames: "Display Frames"; - readonly property string audioScopePause: "Pause Scope"; - readonly property string audioScopeTwentyFrames: "Twenty"; - readonly property string audioStatsShowInjectedStreams: "Audio Stats Show Injected Streams"; - readonly property string audioTools: "Show Level Meter"; - readonly property string autoMuteAudio: "Auto Mute Microphone"; - readonly property string avatarReceiveStats: "Show Receive Stats"; - readonly property string back: "Back"; - readonly property string bandwidthDetails: "Bandwidth Details"; - readonly property string binaryEyelidControl: "Binary Eyelid Control"; - readonly property string bookmarkLocation: "Bookmark Location"; - readonly property string bookmarks: "Bookmarks"; - readonly property string cachesSize: "RAM Caches Size"; - readonly property string calibrateCamera: "Calibrate Camera"; - readonly property string cameraEntityMode: "Entity Mode"; - readonly property string centerPlayerInView: "Center Player In View"; - readonly property string chat: "Chat..."; - readonly property string collisions: "Collisions"; - readonly property string connexion: "Activate 3D Connexion Devices"; - readonly property string console_: "Console..."; - readonly property string controlWithSpeech: "Control With Speech"; - readonly property string copyAddress: "Copy Address to Clipboard"; - readonly property string copyPath: "Copy Path to Clipboard"; - readonly property string coupleEyelids: "Couple Eyelids"; - readonly property string crashInterface: "Crash Interface"; - readonly property string debugAmbientOcclusion: "Debug Ambient Occlusion"; - readonly property string decreaseAvatarSize: "Decrease Avatar Size"; - readonly property string deleteBookmark: "Delete Bookmark..."; - readonly property string disableActivityLogger: "Disable Activity Logger"; - readonly property string disableEyelidAdjustment: "Disable Eyelid Adjustment"; - readonly property string disableLightEntities: "Disable Light Entities"; - readonly property string disableNackPackets: "Disable Entity NACK Packets"; - readonly property string diskCacheEditor: "Disk Cache Editor"; - readonly property string displayCrashOptions: "Display Crash Options"; - readonly property string displayHandTargets: "Show Hand Targets"; - readonly property string displayModelBounds: "Display Model Bounds"; - readonly property string displayModelTriangles: "Display Model Triangles"; - readonly property string displayModelElementChildProxies: "Display Model Element Children"; - readonly property string displayModelElementProxy: "Display Model Element Bounds"; - readonly property string displayDebugTimingDetails: "Display Timing Details"; - readonly property string dontDoPrecisionPicking: "Don't Do Precision Picking"; - readonly property string dontRenderEntitiesAsScene: "Don't Render Entities as Scene"; - readonly property string echoLocalAudio: "Echo Local Audio"; - readonly property string echoServerAudio: "Echo Server Audio"; - readonly property string enable3DTVMode: "Enable 3DTV Mode"; - readonly property string enableCharacterController: "Enable avatar collisions"; - readonly property string expandMyAvatarSimulateTiming: "Expand /myAvatar/simulation"; - readonly property string expandMyAvatarTiming: "Expand /myAvatar"; - readonly property string expandOtherAvatarTiming: "Expand /otherAvatar"; - readonly property string expandPaintGLTiming: "Expand /paintGL"; - readonly property string expandUpdateTiming: "Expand /update"; - readonly property string faceshift: "Faceshift"; - readonly property string firstPerson: "First Person"; - readonly property string fivePointCalibration: "5 Point Calibration"; - readonly property string fixGaze: "Fix Gaze (no saccade)"; - readonly property string forward: "Forward"; - readonly property string frameTimer: "Show Timer"; - readonly property string fullscreenMirror: "Mirror"; - readonly property string help: "Help..."; - readonly property string increaseAvatarSize: "Increase Avatar Size"; - readonly property string independentMode: "Independent Mode"; - readonly property string inputMenu: "Avatar>Input Devices"; - readonly property string keyboardMotorControl: "Enable Keyboard Motor Control"; - readonly property string leapMotionOnHMD: "Leap Motion on HMD"; - readonly property string loadScript: "Open and Run Script File..."; - readonly property string loadScriptURL: "Open and Run Script from URL..."; - readonly property string lodTools: "LOD Tools"; - readonly property string login: "Login"; - readonly property string log: "Log"; - readonly property string logExtraTimings: "Log Extra Timing Details"; - readonly property string lowVelocityFilter: "Low Velocity Filter"; - readonly property string meshVisible: "Draw Mesh"; - readonly property string miniMirror: "Mini Mirror"; - readonly property string muteAudio: "Mute Microphone"; - readonly property string muteEnvironment: "Mute Environment"; - readonly property string muteFaceTracking: "Mute Face Tracking"; - readonly property string namesAboveHeads: "Names Above Heads"; - readonly property string noFaceTracking: "None"; - readonly property string octreeStats: "Entity Statistics"; - readonly property string onePointCalibration: "1 Point Calibration"; - readonly property string onlyDisplayTopTen: "Only Display Top Ten"; - readonly property string outputMenu: "Display"; - readonly property string packageModel: "Package Model..."; - readonly property string pair: "Pair"; - readonly property string physicsShowOwned: "Highlight Simulation Ownership"; - readonly property string physicsShowHulls: "Draw Collision Hulls"; - readonly property string pipelineWarnings: "Log Render Pipeline Warnings"; - readonly property string preferences: "General..."; - readonly property string quit: "Quit"; - readonly property string reloadAllScripts: "Reload All Scripts"; - readonly property string reloadContent: "Reload Content (Clears all caches)"; - readonly property string renderBoundingCollisionShapes: "Show Bounding Collision Shapes"; - readonly property string renderFocusIndicator: "Show Eye Focus"; - readonly property string renderLookAtTargets: "Show Look-at Targets"; - readonly property string renderLookAtVectors: "Show Look-at Vectors"; - readonly property string renderResolution: "Scale Resolution"; - readonly property string renderResolutionOne: "1"; - readonly property string renderResolutionTwoThird: "2/3"; - readonly property string renderResolutionHalf: "1/2"; - readonly property string renderResolutionThird: "1/3"; - readonly property string renderResolutionQuarter: "1/4"; - readonly property string renderAmbientLight: "Ambient Light"; - readonly property string renderAmbientLightGlobal: "Global"; - readonly property string renderAmbientLight0: "OLD_TOWN_SQUARE"; - readonly property string renderAmbientLight1: "GRACE_CATHEDRAL"; - readonly property string renderAmbientLight2: "EUCALYPTUS_GROVE"; - readonly property string renderAmbientLight3: "ST_PETERS_BASILICA"; - readonly property string renderAmbientLight4: "UFFIZI_GALLERY"; - readonly property string renderAmbientLight5: "GALILEOS_TOMB"; - readonly property string renderAmbientLight6: "VINE_STREET_KITCHEN"; - readonly property string renderAmbientLight7: "BREEZEWAY"; - readonly property string renderAmbientLight8: "CAMPUS_SUNSET"; - readonly property string renderAmbientLight9: "FUNSTON_BEACH_SUNSET"; - readonly property string resetAvatarSize: "Reset Avatar Size"; - readonly property string resetSensors: "Reset Sensors"; - readonly property string runningScripts: "Running Scripts..."; - readonly property string runTimingTests: "Run Timing Tests"; - readonly property string scriptEditor: "Script Editor..."; - readonly property string scriptedMotorControl: "Enable Scripted Motor Control"; - readonly property string showDSConnectTable: "Show Domain Connection Timing"; - readonly property string showBordersEntityNodes: "Show Entity Nodes"; - readonly property string showRealtimeEntityStats: "Show Realtime Entity Stats"; - readonly property string showWhosLookingAtMe: "Show Who's Looking at Me"; - readonly property string standingHMDSensorMode: "Standing HMD Sensor Mode"; - readonly property string simulateEyeTracking: "Simulate"; - readonly property string sMIEyeTracking: "SMI Eye Tracking"; - readonly property string stars: "Stars"; - readonly property string stats: "Stats"; - readonly property string stopAllScripts: "Stop All Scripts"; - readonly property string suppressShortTimings: "Suppress Timings Less than 10ms"; - readonly property string thirdPerson: "Third Person"; - readonly property string threePointCalibration: "3 Point Calibration"; - readonly property string throttleFPSIfNotFocus: "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp - readonly property string toolWindow: "Tool Window"; - readonly property string transmitterDrive: "Transmitter Drive"; - readonly property string turnWithHead: "Turn using Head"; - readonly property string useAudioForMouth: "Use Audio for Mouth"; - readonly property string useCamera: "Use Camera"; - readonly property string velocityFilter: "Velocity Filter"; - readonly property string visibleToEveryone: "Everyone"; - readonly property string visibleToFriends: "Friends"; - readonly property string visibleToNoOne: "No one"; - readonly property string worldAxes: "World Axes"; -} - diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9e3bd0c69c..b0e4880011 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -108,7 +108,6 @@ #include "audio/AudioScope.h" #include "avatar/AvatarManager.h" #include "CrashHandler.h" -#include "input-plugins/SpacemouseManager.h" #include "devices/DdeFaceTracker.h" #include "devices/EyeTracker.h" #include "devices/Faceshift.h" @@ -199,6 +198,7 @@ static const float PHYSICS_READY_RANGE = 3.0f; // how far from avatar to check f static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); +static const QString INPUT_DEVICE_MENU_PREFIX = "Device: "; Setting::Handle maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS); const QHash Application::_acceptedExtensions { @@ -762,6 +762,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Tell our entity edit sender about our known jurisdictions _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions); + _entityEditSender.setMyAvatar(getMyAvatar()); // For now we're going to set the PPS for outbound packets to be super high, this is // probably not the right long term solution. But for now, we're going to do this to @@ -997,7 +998,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); if (webEntity) { webEntity->setProxyWindow(_window->windowHandle()); - if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + if (Menu::getInstance()->isOptionChecked(INPUT_DEVICE_MENU_PREFIX + KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->pluginFocusOutEvent(); } _keyboardFocusedItem = entityItemID; @@ -1050,6 +1051,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Make sure we don't time out during slow operations at startup updateHeartbeat(); + OctreeEditPacketSender* packetSender = entityScriptingInterface->getPacketSender(); + EntityEditPacketSender* entityPacketSender = static_cast(packetSender); + entityPacketSender->setMyAvatar(getMyAvatar()); + connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); @@ -1121,7 +1126,7 @@ void Application::aboutToQuit() { emit beforeAboutToQuit(); foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { - QString name = inputPlugin->getName(); + QString name = INPUT_DEVICE_MENU_PREFIX + inputPlugin->getName(); QAction* action = Menu::getInstance()->getActionForOption(name); if (action->isChecked()) { inputPlugin->deactivate(); @@ -1440,8 +1445,7 @@ void Application::initializeUi() { // This will set up the input plugins UI _activeInputPlugins.clear(); foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { - QString name = inputPlugin->getName(); - if (name == KeyboardMouseDevice::NAME) { + if (KeyboardMouseDevice::NAME == inputPlugin->getName()) { _keyboardMouseDevice = std::dynamic_pointer_cast(inputPlugin); } } @@ -1510,17 +1514,17 @@ void Application::paintGL() { renderArgs._context->syncCache(); } - if (Menu::getInstance()->isOptionChecked(MenuOption::MiniMirror)) { + auto inputs = AvatarInputs::getInstance(); + if (inputs->mirrorVisible()) { PerformanceTimer perfTimer("Mirror"); auto primaryFbo = DependencyManager::get()->getPrimaryFramebuffer(); renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; renderArgs._blitFramebuffer = DependencyManager::get()->getSelfieFramebuffer(); - auto inputs = AvatarInputs::getInstance(); _mirrorViewRect.moveTo(inputs->x(), inputs->y()); - renderRearViewMirror(&renderArgs, _mirrorViewRect); + renderRearViewMirror(&renderArgs, _mirrorViewRect, inputs->mirrorZoomed()); renderArgs._blitFramebuffer.reset(); renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; @@ -1822,25 +1826,37 @@ bool Application::event(QEvent* event) { return false; } - static bool justPresented = false; + // Presentation/painting logic + // TODO: Decouple presentation and painting loops + static bool isPainting = false; if ((int)event->type() == (int)Present) { - if (justPresented) { - justPresented = false; - - // If presentation is hogging the main thread, repost as low priority to avoid hanging the GUI. + if (isPainting) { + // If painting (triggered by presentation) is hogging the main thread, + // repost as low priority to avoid hanging the GUI. // This has the effect of allowing presentation to exceed the paint budget by X times and - // only dropping every (1/X) frames, instead of every ceil(X) frames. + // only dropping every (1/X) frames, instead of every ceil(X) frames // (e.g. at a 60FPS target, painting for 17us would fall to 58.82FPS instead of 30FPS). removePostedEvents(this, Present); postEvent(this, new QEvent(static_cast(Present)), Qt::LowEventPriority); + isPainting = false; return true; } idle(); + + postEvent(this, new QEvent(static_cast(Paint)), Qt::HighEventPriority); + isPainting = true; + return true; } else if ((int)event->type() == (int)Paint) { - justPresented = true; + // NOTE: This must be updated as close to painting as possible, + // or AvatarInputs will mysteriously move to the bottom-right + AvatarInputs::getInstance()->update(); + paintGL(); + + isPainting = false; + return true; } @@ -1973,7 +1989,7 @@ void Application::keyPressEvent(QKeyEvent* event) { } if (hasFocus()) { - if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + if (Menu::getInstance()->isOptionChecked(INPUT_DEVICE_MENU_PREFIX + KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->keyPressEvent(event); } @@ -2307,7 +2323,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { return; } - if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + if (Menu::getInstance()->isOptionChecked(INPUT_DEVICE_MENU_PREFIX + KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->keyReleaseEvent(event); } @@ -2339,7 +2355,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { void Application::focusOutEvent(QFocusEvent* event) { auto inputPlugins = PluginManager::getInstance()->getInputPlugins(); foreach(auto inputPlugin, inputPlugins) { - QString name = inputPlugin->getName(); + QString name = INPUT_DEVICE_MENU_PREFIX + inputPlugin->getName(); QAction* action = Menu::getInstance()->getActionForOption(name); if (action && action->isChecked()) { inputPlugin->pluginFocusOutEvent(); @@ -2426,7 +2442,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { return; } - if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + if (Menu::getInstance()->isOptionChecked(INPUT_DEVICE_MENU_PREFIX + KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->mouseMoveEvent(event); } @@ -2463,7 +2479,7 @@ void Application::mousePressEvent(QMouseEvent* event) { if (hasFocus()) { - if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + if (Menu::getInstance()->isOptionChecked(INPUT_DEVICE_MENU_PREFIX + KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->mousePressEvent(event); } @@ -2508,7 +2524,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { } if (hasFocus()) { - if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + if (Menu::getInstance()->isOptionChecked(INPUT_DEVICE_MENU_PREFIX + KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->mouseReleaseEvent(event); } @@ -2535,7 +2551,7 @@ void Application::touchUpdateEvent(QTouchEvent* event) { return; } - if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + if (Menu::getInstance()->isOptionChecked(INPUT_DEVICE_MENU_PREFIX + KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->touchUpdateEvent(event); } } @@ -2553,7 +2569,7 @@ void Application::touchBeginEvent(QTouchEvent* event) { return; } - if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + if (Menu::getInstance()->isOptionChecked(INPUT_DEVICE_MENU_PREFIX + KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->touchBeginEvent(event); } @@ -2570,7 +2586,7 @@ void Application::touchEndEvent(QTouchEvent* event) { return; } - if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + if (Menu::getInstance()->isOptionChecked(INPUT_DEVICE_MENU_PREFIX + KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->touchEndEvent(event); } @@ -2586,7 +2602,7 @@ void Application::wheelEvent(QWheelEvent* event) const { return; } - if (Menu::getInstance()->isOptionChecked(KeyboardMouseDevice::NAME)) { + if (Menu::getInstance()->isOptionChecked(INPUT_DEVICE_MENU_PREFIX + KeyboardMouseDevice::NAME)) { _keyboardMouseDevice->wheelEvent(event); } } @@ -2655,9 +2671,6 @@ void Application::idle() { // Sync up the _renderedFrameIndex _renderedFrameIndex = displayPlugin->presentCount(); - // Request a paint ASAP - postEvent(this, new QEvent(static_cast(Paint)), Qt::HighEventPriority + 1); - // Update the deadlock watchdog updateHeartbeat(); @@ -2670,9 +2683,6 @@ void Application::idle() { firstIdle = false; connect(offscreenUi.data(), &OffscreenUi::showDesktop, this, &Application::showDesktop); _overlayConductor.setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Overlays)); - } else { - // FIXME: AvatarInputs are positioned incorrectly if instantiated before the first paint - AvatarInputs::getInstance()->update(); } PROFILE_RANGE(__FUNCTION__); @@ -2687,8 +2697,6 @@ void Application::idle() { _keyboardDeviceHasFocus = true; } - - // We're going to execute idle processing, so restart the last idle timer _lastTimeUpdated.start(); @@ -2726,7 +2734,7 @@ void Application::idle() { getActiveDisplayPlugin()->idle(); auto inputPlugins = PluginManager::getInstance()->getInputPlugins(); foreach(auto inputPlugin, inputPlugins) { - QString name = inputPlugin->getName(); + QString name = INPUT_DEVICE_MENU_PREFIX + inputPlugin->getName(); QAction* action = Menu::getInstance()->getActionForOption(name); if (action && action->isChecked()) { inputPlugin->idle(); @@ -3369,22 +3377,18 @@ void Application::update(float deltaTime) { }; InputPluginPointer keyboardMousePlugin; - bool jointsCaptured = false; for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { if (inputPlugin->getName() == KeyboardMouseDevice::NAME) { keyboardMousePlugin = inputPlugin; } else if (inputPlugin->isActive()) { - inputPlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured); - if (inputPlugin->isJointController()) { - jointsCaptured = true; - } + inputPlugin->pluginUpdate(deltaTime, calibrationData); } } userInputMapper->update(deltaTime); if (keyboardMousePlugin && keyboardMousePlugin->isActive()) { - keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData, jointsCaptured); + keyboardMousePlugin->pluginUpdate(deltaTime, calibrationData); } _controllerScriptingInterface->updateInputControllers(); @@ -3590,10 +3594,6 @@ void Application::update(float deltaTime) { int Application::sendNackPackets() { - if (Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { - return 0; - } - // iterates through all nodes in NodeList auto nodeList = DependencyManager::get(); @@ -4101,7 +4101,7 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se activeRenderingThread = nullptr; } -void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region) { +void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed) { auto originalViewport = renderArgs->_viewport; // Grab current viewport to reset it at the end @@ -4111,7 +4111,7 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi auto myAvatar = getMyAvatar(); // bool eyeRelativeCamera = false; - if (!AvatarInputs::getInstance()->mirrorZoomed()) { + if (!isZoomed) { _mirrorCamera.setPosition(myAvatar->getChestPosition() + myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * myAvatar->getScale()); @@ -4195,6 +4195,7 @@ void Application::clearDomainOctreeDetails() { qCDebug(interfaceapp) << "Clearing domain octree details..."; resetPhysicsReadyInformation(); + getMyAvatar()->setAvatarEntityDataChanged(true); // to recreate worn entities // reset our node to stats and node to jurisdiction maps... since these must be changing... _entityServerJurisdictions.withWriteLock([&] { @@ -5146,21 +5147,23 @@ void Application::updateDisplayMode() { Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin"); } -static void addInputPluginToMenu(InputPluginPointer inputPlugin, bool active = false) { +static void addInputPluginToMenu(InputPluginPointer inputPlugin) { auto menu = Menu::getInstance(); - QString name = inputPlugin->getName(); + QString name = INPUT_DEVICE_MENU_PREFIX + inputPlugin->getName(); Q_ASSERT(!menu->menuItemExists(MenuOption::InputMenu, name)); static QActionGroup* inputPluginGroup = nullptr; if (!inputPluginGroup) { inputPluginGroup = new QActionGroup(menu); + inputPluginGroup->setExclusive(false); } + auto parent = menu->getMenu(MenuOption::InputMenu); auto action = menu->addCheckableActionToQMenuAndActionHash(parent, - name, 0, active, qApp, + name, 0, true, qApp, SLOT(updateInputModes())); + inputPluginGroup->addAction(action); - inputPluginGroup->setExclusive(false); Q_ASSERT(menu->menuItemExists(MenuOption::InputMenu, name)); } @@ -5170,10 +5173,8 @@ void Application::updateInputModes() { auto inputPlugins = PluginManager::getInstance()->getInputPlugins(); static std::once_flag once; std::call_once(once, [&] { - bool first = true; foreach(auto inputPlugin, inputPlugins) { - addInputPluginToMenu(inputPlugin, first); - first = false; + addInputPluginToMenu(inputPlugin); } }); auto offscreenUi = DependencyManager::get(); @@ -5181,7 +5182,7 @@ void Application::updateInputModes() { InputPluginList newInputPlugins; InputPluginList removedInputPlugins; foreach(auto inputPlugin, inputPlugins) { - QString name = inputPlugin->getName(); + QString name = INPUT_DEVICE_MENU_PREFIX + inputPlugin->getName(); QAction* action = menu->getActionForOption(name); auto it = std::find(std::begin(_activeInputPlugins), std::end(_activeInputPlugins), inputPlugin); diff --git a/interface/src/Application.h b/interface/src/Application.h index 5616722c92..28dbcead47 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -342,7 +342,7 @@ private: glm::vec3 getSunDirection() const; - void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region); + void renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed); int sendNackPackets(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8b69bb8022..538410a47d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -34,7 +34,6 @@ #include "avatar/AvatarManager.h" #include "devices/DdeFaceTracker.h" #include "devices/Faceshift.h" -#include "input-plugins/SpacemouseManager.h" #include "MainWindow.h" #include "render/DrawStatus.h" #include "scripting/MenuScriptingInterface.h" @@ -327,12 +326,6 @@ Menu::Menu() { connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool))); #endif - // Settings > Input Devices - MenuWrapper* inputModeMenu = addMenu(MenuOption::InputMenu, "Advanced"); - QActionGroup* inputModeGroup = new QActionGroup(inputModeMenu); - inputModeGroup->setExclusive(false); - - // Developer menu ---------------------------------- MenuWrapper* developerMenu = addMenu("Developer", "Developer"); @@ -410,6 +403,12 @@ Menu::Menu() { // Developer > Avatar >>> MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar"); + // Settings > Input Devices + MenuWrapper* inputModeMenu = addMenu(MenuOption::InputMenu, "Advanced"); + QActionGroup* inputModeGroup = new QActionGroup(inputModeMenu); + inputModeGroup->setExclusive(false); + + // Developer > Avatar > Face Tracking MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking"); { @@ -531,9 +530,6 @@ Menu::Menu() { // Developer > Network >>> MenuWrapper* networkMenu = developerMenu->addMenu("Network"); addActionToQMenuAndActionHash(networkMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches())); - addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false, - qApp->getEntityEditPacketSender(), - SLOT(toggleNackPackets())); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableActivityLogger, 0, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 484be9f346..36d285e2cf 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -84,7 +84,6 @@ namespace MenuOption { const QString DisableActivityLogger = "Disable Activity Logger"; const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; const QString DisableLightEntities = "Disable Light Entities"; - const QString DisableNackPackets = "Disable Entity NACK Packets"; const QString DiskCacheEditor = "Disk Cache Editor"; const QString DisplayCrashOptions = "Display Crash Options"; const QString DisplayHandTargets = "Show Hand Targets"; @@ -114,7 +113,7 @@ namespace MenuOption { const QString Help = "Help..."; const QString IncreaseAvatarSize = "Increase Avatar Size"; const QString IndependentMode = "Independent Mode"; - const QString InputMenu = "Avatar>Input Devices"; + const QString InputMenu = "Developer>Avatar>Input Devices"; const QString ActionMotorControl = "Enable Default Motor Control"; const QString LeapMotionOnHMD = "Leap Motion on HMD"; const QString LoadScript = "Open and Run Script File..."; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 9ae636af36..d12306a122 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "Application.h" @@ -102,6 +103,18 @@ Avatar::Avatar(RigPointer rig) : Avatar::~Avatar() { assert(isDead()); // mark dead before calling the dtor + + EntityTreeRenderer* treeRenderer = qApp->getEntities(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + if (entityTree) { + entityTree->withWriteLock([&] { + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + entityTree->deleteEntity(entityID, true, true); + } + }); + } + if (_motionState) { delete _motionState; _motionState = nullptr; @@ -157,6 +170,90 @@ void Avatar::animateScaleChanges(float deltaTime) { } } +void Avatar::updateAvatarEntities() { + // - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity() + // - updateAvatarEntity saves the bytes and sets _avatarEntityDataLocallyEdited + // - MyAvatar::update notices _avatarEntityDataLocallyEdited and calls sendIdentityPacket + // - sendIdentityPacket sends the entity bytes to the server which relays them to other interfaces + // - AvatarHashMap::processAvatarIdentityPacket on other interfaces call avatar->setAvatarEntityData() + // - setAvatarEntityData saves the bytes and sets _avatarEntityDataChanged = true + // - (My)Avatar::simulate notices _avatarEntityDataChanged and here we are... + + if (!_avatarEntityDataChanged) { + return; + } + + if (getID() == QUuid()) { + return; // wait until MyAvatar gets an ID before doing this. + } + + EntityTreeRenderer* treeRenderer = qApp->getEntities(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + if (!entityTree) { + return; + } + + bool success = true; + QScriptEngine scriptEngine; + entityTree->withWriteLock([&] { + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + // see EntityEditPacketSender::queueEditEntityMessage for the other end of this. unpack properties + // and either add or update the entity. + QByteArray jsonByteArray = avatarEntities.value(entityID); + QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(jsonByteArray); + if (!jsonProperties.isObject()) { + qCDebug(interfaceapp) << "got bad avatarEntity json" << QString(jsonByteArray.toHex()); + continue; + } + + QVariant variantProperties = jsonProperties.toVariant(); + QVariantMap asMap = variantProperties.toMap(); + QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine); + EntityItemProperties properties; + EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, properties); + properties.setClientOnly(true); + properties.setOwningAvatarID(getID()); + + // there's no entity-server to tell us we're the simulation owner, so always set the + // simulationOwner to the owningAvatarID and a high priority. + properties.setSimulationOwner(getID(), AVATAR_ENTITY_SIMULATION_PRIORITY); + + if (properties.getParentID() == AVATAR_SELF_ID) { + properties.setParentID(getID()); + } + + EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); + + if (entity) { + if (entityTree->updateEntity(entityID, properties)) { + entity->updateLastEditedFromRemote(); + } else { + success = false; + } + } else { + entity = entityTree->addEntity(entityID, properties); + if (!entity) { + success = false; + } + } + } + + AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs(); + foreach (auto entityID, recentlyDettachedAvatarEntities) { + if (!_avatarEntityData.contains(entityID)) { + entityTree->deleteEntity(entityID, true, true); + } + } + }); + + if (success) { + setAvatarEntityDataChanged(false); + } +} + + + void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); @@ -229,6 +326,7 @@ void Avatar::simulate(float deltaTime) { simulateAttachments(deltaTime); updatePalms(); + updateAvatarEntities(); } bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const { @@ -1088,7 +1186,7 @@ void Avatar::setParentID(const QUuid& parentID) { if (success) { setTransform(beforeChangeTransform, success); if (!success) { - qDebug() << "Avatar::setParentID failed to reset avatar's location."; + qCDebug(interfaceapp) << "Avatar::setParentID failed to reset avatar's location."; } } } @@ -1103,7 +1201,7 @@ void Avatar::setParentJointIndex(quint16 parentJointIndex) { if (success) { setTransform(beforeChangeTransform, success); if (!success) { - qDebug() << "Avatar::setParentJointIndex failed to reset avatar's location."; + qCDebug(interfaceapp) << "Avatar::setParentJointIndex failed to reset avatar's location."; } } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 2580ac1d37..7072ce3847 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -64,6 +64,7 @@ public: typedef std::shared_ptr PayloadPointer; void init(); + void updateAvatarEntities(); void simulate(float deltaTime); virtual void simulateAttachments(float deltaTime); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7d08367c8d..2366d078bd 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -309,6 +309,10 @@ void MyAvatar::update(float deltaTime) { head->setAudioLoudness(audio->getLastInputLoudness()); head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); + if (_avatarEntityDataLocallyEdited) { + sendIdentityPacket(); + } + simulate(deltaTime); currentEnergy += energyChargeRate; @@ -424,7 +428,14 @@ void MyAvatar::simulate(float deltaTime) { EntityTreeRenderer* entityTreeRenderer = qApp->getEntities(); EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; if (entityTree) { + bool flyingAllowed = true; + bool ghostingAllowed = true; entityTree->withWriteLock([&] { + std::shared_ptr zone = entityTreeRenderer->myAvatarZone(); + if (zone) { + flyingAllowed = zone->getFlyingAllowed(); + ghostingAllowed = zone->getGhostingAllowed(); + } auto now = usecTimestampNow(); EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); MovingEntitiesOperator moveOperator(entityTree); @@ -441,7 +452,8 @@ void MyAvatar::simulate(float deltaTime) { EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); properties.setLastEdited(now); - packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties); + + packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entity->getID(), properties); entity->setLastBroadcast(usecTimestampNow()); } } @@ -452,7 +464,13 @@ void MyAvatar::simulate(float deltaTime) { entityTree->recurseTreeWithOperator(&moveOperator); } }); + _characterController.setFlyingAllowed(flyingAllowed); + if (!_characterController.isEnabled() && !ghostingAllowed) { + _characterController.setEnabled(true); + } } + + updateAvatarEntities(); } // thread-safe @@ -700,6 +718,16 @@ void MyAvatar::saveData() { } settings.endArray(); + settings.beginWriteArray("avatarEntityData"); + int avatarEntityIndex = 0; + for (auto entityID : _avatarEntityData.keys()) { + settings.setArrayIndex(avatarEntityIndex); + settings.setValue("id", entityID); + settings.setValue("properties", _avatarEntityData.value(entityID)); + avatarEntityIndex++; + } + settings.endArray(); + settings.setValue("displayName", _displayName); settings.setValue("collisionSoundURL", _collisionSoundURL); settings.setValue("useSnapTurn", _useSnapTurn); @@ -811,6 +839,17 @@ void MyAvatar::loadData() { settings.endArray(); setAttachmentData(attachmentData); + int avatarEntityCount = settings.beginReadArray("avatarEntityData"); + for (int i = 0; i < avatarEntityCount; i++) { + settings.setArrayIndex(i); + QUuid entityID = settings.value("id").toUuid(); + // QUuid entityID = QUuid::createUuid(); // generate a new ID + QByteArray properties = settings.value("properties").toByteArray(); + updateAvatarEntity(entityID, properties); + } + settings.endArray(); + setAvatarEntityDataChanged(true); + setDisplayName(settings.value("displayName").toString()); setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool()); @@ -1788,7 +1827,21 @@ void MyAvatar::updateMotionBehaviorFromMenu() { } else { _motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; } - _characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController)); + + bool ghostingAllowed = true; + EntityTreeRenderer* entityTreeRenderer = qApp->getEntities(); + if (entityTreeRenderer) { + std::shared_ptr zone = entityTreeRenderer->myAvatarZone(); + if (zone) { + ghostingAllowed = zone->getGhostingAllowed(); + } + } + bool checked = menu->isOptionChecked(MenuOption::EnableCharacterController); + if (!ghostingAllowed) { + checked = true; + } + + _characterController.setEnabled(checked); } void MyAvatar::clearDriveKeys() { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 9c556dc42b..b26cecbc9e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -955,14 +955,16 @@ void AvatarData::clearJointsData() { } bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray& data) { + // this is used by the avatar-mixer QDataStream packetStream(data); QUuid avatarUUID; QUrl unusedModelURL; // legacy faceModel support QUrl skeletonModelURL; QVector attachmentData; + AvatarEntityMap avatarEntityData; QString displayName; - packetStream >> avatarUUID >> unusedModelURL >> skeletonModelURL >> attachmentData >> displayName; + packetStream >> avatarUUID >> unusedModelURL >> skeletonModelURL >> attachmentData >> displayName >> avatarEntityData; bool hasIdentityChanged = false; @@ -982,6 +984,11 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray& data) { hasIdentityChanged = true; } + if (avatarEntityData != _avatarEntityData) { + setAvatarEntityData(avatarEntityData); + hasIdentityChanged = true; + } + return hasIdentityChanged; } @@ -993,7 +1000,7 @@ QByteArray AvatarData::identityByteArray() { QUrl unusedModelURL; // legacy faceModel support - identityStream << QUuid() << unusedModelURL << urlToSend << _attachmentData << _displayName; + identityStream << QUuid() << unusedModelURL << urlToSend << _attachmentData << _displayName << _avatarEntityData; return identityData; } @@ -1167,6 +1174,8 @@ void AvatarData::sendIdentityPacket() { [&](const SharedNodePointer& node) { nodeList->sendPacketList(std::move(packetList), *node); }); + + _avatarEntityDataLocallyEdited = false; } void AvatarData::updateJointMappings() { @@ -1339,6 +1348,7 @@ static const QString JSON_AVATAR_HEAD_MODEL = QStringLiteral("headModel"); static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel"); static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName"); static const QString JSON_AVATAR_ATTACHEMENTS = QStringLiteral("attachments"); +static const QString JSON_AVATAR_ENTITIES = QStringLiteral("attachedEntities"); static const QString JSON_AVATAR_SCALE = QStringLiteral("scale"); QJsonValue toJsonValue(const JointData& joint) { @@ -1377,6 +1387,17 @@ QJsonObject AvatarData::toJson() const { root[JSON_AVATAR_ATTACHEMENTS] = attachmentsJson; } + if (!_avatarEntityData.empty()) { + QJsonArray avatarEntityJson; + for (auto entityID : _avatarEntityData.keys()) { + QVariantMap entityData; + entityData.insert("id", entityID); + entityData.insert("properties", _avatarEntityData.value(entityID)); + avatarEntityJson.push_back(QVariant(entityData).toJsonObject()); + } + root[JSON_AVATAR_ENTITIES] = avatarEntityJson; + } + auto recordingBasis = getRecordingBasis(); bool success; Transform avatarTransform = getTransform(success); @@ -1476,6 +1497,13 @@ void AvatarData::fromJson(const QJsonObject& json) { setAttachmentData(attachments); } + // if (json.contains(JSON_AVATAR_ENTITIES) && json[JSON_AVATAR_ENTITIES].isArray()) { + // QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHEMENTS].toArray(); + // for (auto attachmentJson : attachmentsJson) { + // // TODO -- something + // } + // } + // Joint rotations are relative to the avatar, so they require no basis correction if (json.contains(JSON_AVATAR_JOINT_ARRAY)) { QVector jointArray; @@ -1628,9 +1656,69 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) { QVector newAttachments; newAttachments.reserve(variant.size()); for (const auto& attachmentVar : variant) { - AttachmentData attachment; + AttachmentData attachment; attachment.fromVariant(attachmentVar); newAttachments.append(attachment); } setAttachmentData(newAttachments); } + +void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updateAvatarEntity", Q_ARG(const QUuid&, entityID), Q_ARG(QByteArray, entityData)); + return; + } + _avatarEntityData.insert(entityID, entityData); + _avatarEntityDataLocallyEdited = true; +} + +void AvatarData::clearAvatarEntity(const QUuid& entityID) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "clearAvatarEntity", Q_ARG(const QUuid&, entityID)); + return; + } + _avatarEntityData.remove(entityID); + _avatarEntityDataLocallyEdited = true; +} + +AvatarEntityMap AvatarData::getAvatarEntityData() const { + if (QThread::currentThread() != thread()) { + AvatarEntityMap result; + QMetaObject::invokeMethod(const_cast(this), "getAvatarEntityData", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(AvatarEntityMap, result)); + return result; + } + return _avatarEntityData; +} + +void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setAvatarEntityData", Q_ARG(const AvatarEntityMap&, avatarEntityData)); + return; + } + if (_avatarEntityData != avatarEntityData) { + // keep track of entities that were attached to this avatar but no longer are + AvatarEntityIDs previousAvatarEntityIDs = QSet::fromList(_avatarEntityData.keys()); + + _avatarEntityData = avatarEntityData; + setAvatarEntityDataChanged(true); + + foreach (auto entityID, previousAvatarEntityIDs) { + if (!_avatarEntityData.contains(entityID)) { + _avatarEntityDetached.insert(entityID); + } + } + } +} + +AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() { + if (QThread::currentThread() != thread()) { + AvatarEntityIDs result; + QMetaObject::invokeMethod(const_cast(this), "getRecentlyDetachedIDs", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(AvatarEntityIDs, result)); + return result; + } + AvatarEntityIDs result = _avatarEntityDetached; + _avatarEntityDetached.clear(); + return result; +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index e9dc100a7c..2402a052c6 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -61,6 +61,8 @@ typedef unsigned long long quint64; using AvatarSharedPointer = std::shared_ptr; using AvatarWeakPointer = std::weak_ptr; using AvatarHash = QHash; +using AvatarEntityMap = QMap; +using AvatarEntityIDs = QSet; using AvatarDataSequenceNumber = uint16_t; @@ -134,6 +136,10 @@ class AttachmentData; class Transform; using TransformPointer = std::shared_ptr; +// When writing out avatarEntities to a QByteArray, if the parentID is the ID of MyAvatar, use this ID instead. This allows +// the value to be reset when the sessionID changes. +const QUuid AVATAR_SELF_ID = QUuid("{00000000-0000-0000-0000-000000000001}"); + class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT @@ -272,6 +278,9 @@ public: Q_INVOKABLE QVariantList getAttachmentsVariant() const; Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant); + Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); + Q_INVOKABLE void clearAvatarEntity(const QUuid& entityID); + void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; } // key state @@ -323,6 +332,11 @@ public: glm::vec3 getClientGlobalPosition() { return _globalPosition; } + Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const; + Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); + void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } + AvatarEntityIDs getAndClearRecentlyDetachedIDs(); + public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); @@ -390,6 +404,11 @@ protected: // updates about one avatar to another. glm::vec3 _globalPosition; + AvatarEntityIDs _avatarEntityDetached; // recently detached from this avatar + AvatarEntityMap _avatarEntityData; + bool _avatarEntityDataLocallyEdited { false }; + bool _avatarEntityDataChanged { false }; + private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index f14e2b0ad3..bd43560ae8 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -107,21 +107,23 @@ void AvatarHashMap::processAvatarDataPacket(QSharedPointer mess } void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode) { + // this is used by clients // setup a data stream to parse the packet QDataStream identityStream(message->getMessage()); QUuid sessionUUID; - + while (!identityStream.atEnd()) { QUrl faceMeshURL, skeletonURL; QVector attachmentData; + AvatarEntityMap avatarEntityData; QString displayName; - identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName; + identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName >> avatarEntityData; // mesh URL for a UUID, find avatar in our list auto avatar = newOrExistingAvatar(sessionUUID, sendingNode); - + if (avatar->getSkeletonModelURL().isEmpty() || (avatar->getSkeletonModelURL() != skeletonURL)) { avatar->setSkeletonModelURL(skeletonURL); // Will expand "" to default and so will not continuously fire } @@ -130,6 +132,8 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer avatar->setAttachmentData(attachmentData); } + avatar->setAvatarEntityData(avatarEntityData); + if (avatar->getDisplayName() != displayName) { avatar->setDisplayName(displayName); } diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index dba856cbaa..79ff4ecbf8 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -121,16 +121,8 @@ namespace controller { return availableInputs; } - void ActionsDevice::update(float deltaTime, const InputCalibrationData& inpuCalibrationData, bool jointsCaptured) { - } - - void ActionsDevice::focusOutEvent() { - } - ActionsDevice::ActionsDevice() : InputDevice("Actions") { _deviceID = UserInputMapper::ACTIONS_DEVICE; } - ActionsDevice::~ActionsDevice() {} - } diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index efdc45cb3d..724d17d951 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -109,13 +109,11 @@ class ActionsDevice : public QObject, public InputDevice { Q_PROPERTY(QString name READ getName) public: - virtual EndpointPointer createEndpoint(const Input& input) const override; - virtual Input::NamedVector getAvailableInputs() const override; - virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; - virtual void focusOutEvent() override; - ActionsDevice(); - virtual ~ActionsDevice(); + + EndpointPointer createEndpoint(const Input& input) const override; + Input::NamedVector getAvailableInputs() const override; + }; } diff --git a/libraries/controllers/src/controllers/InputDevice.h b/libraries/controllers/src/controllers/InputDevice.h index 93247965bc..afb1f7d1f7 100644 --- a/libraries/controllers/src/controllers/InputDevice.h +++ b/libraries/controllers/src/controllers/InputDevice.h @@ -57,9 +57,9 @@ public: // Update call MUST be called once per simulation loop // It takes care of updating the action states and deltas - virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) = 0; + virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData) {}; - virtual void focusOutEvent() = 0; + virtual void focusOutEvent() {}; int getDeviceID() { return _deviceID; } void setDeviceID(int deviceID) { _deviceID = deviceID; } diff --git a/libraries/controllers/src/controllers/StandardController.cpp b/libraries/controllers/src/controllers/StandardController.cpp index 5996cad5df..a9d317d407 100644 --- a/libraries/controllers/src/controllers/StandardController.cpp +++ b/libraries/controllers/src/controllers/StandardController.cpp @@ -22,12 +22,6 @@ StandardController::StandardController() : InputDevice("Standard") { _deviceID = UserInputMapper::STANDARD_DEVICE; } -StandardController::~StandardController() { -} - -void StandardController::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { -} - void StandardController::focusOutEvent() { _axisStateMap.clear(); _buttonPressedMap.clear(); diff --git a/libraries/controllers/src/controllers/StandardController.h b/libraries/controllers/src/controllers/StandardController.h index fee608f822..58c3ecaa30 100644 --- a/libraries/controllers/src/controllers/StandardController.h +++ b/libraries/controllers/src/controllers/StandardController.h @@ -28,11 +28,9 @@ public: virtual EndpointPointer createEndpoint(const Input& input) const override; virtual Input::NamedVector getAvailableInputs() const override; virtual QStringList getDefaultMappingConfigs() const override; - virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; virtual void focusOutEvent() override; StandardController(); - virtual ~StandardController(); }; } diff --git a/libraries/controllers/src/controllers/StateController.h b/libraries/controllers/src/controllers/StateController.h index 57414c3ae8..c18c9df27c 100644 --- a/libraries/controllers/src/controllers/StateController.h +++ b/libraries/controllers/src/controllers/StateController.h @@ -35,10 +35,7 @@ public: const QString& getName() const { return _name; } // Device functions - virtual Input::NamedVector getAvailableInputs() const override; - - void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) override {} - void focusOutEvent() override {} + Input::NamedVector getAvailableInputs() const override; void setInputVariant(const QString& name, ReadLambda lambda); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 5c06c5f5cc..b0d0d2bacc 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -88,6 +88,8 @@ public: // For Scene.shouldRenderEntities QList& getEntitiesLastInScene() { return _entityIDsLastInScene; } + std::shared_ptr myAvatarZone() { return _bestZone; } + signals: void mousePressOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event); void mousePressOffEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event); diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index d148145dde..011675fc82 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -47,6 +47,9 @@ namespace render { } void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters) { + auto nodeList = DependencyManager::get(); + const QUuid& myNodeID = nodeList->getSessionUUID(); + statusGetters.push_back([entity] () -> render::Item::Status::Value { quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote(); const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND); @@ -81,9 +84,7 @@ void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status: (unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET); }); - statusGetters.push_back([entity] () -> render::Item::Status::Value { - auto nodeList = DependencyManager::get(); - const QUuid& myNodeID = nodeList->getSessionUUID(); + statusGetters.push_back([entity, myNodeID] () -> render::Item::Status::Value { bool weOwnSimulation = entity->getSimulationOwner().matchesValidID(myNodeID); bool otherOwnSimulation = !weOwnSimulation && !entity->getSimulationOwner().isNull(); @@ -106,4 +107,18 @@ void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status: return render::Item::Status::Value(0.0f, render::Item::Status::Value::GREEN, (unsigned char)RenderItemStatusIcon::HAS_ACTIONS); }); + + statusGetters.push_back([entity, myNodeID] () -> render::Item::Status::Value { + if (entity->getClientOnly()) { + if (entity->getOwningAvatarID() == myNodeID) { + return render::Item::Status::Value(1.0f, render::Item::Status::Value::GREEN, + (unsigned char)RenderItemStatusIcon::CLIENT_ONLY); + } else { + return render::Item::Status::Value(1.0f, render::Item::Status::Value::RED, + (unsigned char)RenderItemStatusIcon::CLIENT_ONLY); + } + } + return render::Item::Status::Value(0.0f, render::Item::Status::Value::GREEN, + (unsigned char)RenderItemStatusIcon::CLIENT_ONLY); + }); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 09451e87d4..9840bf3150 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -26,6 +26,7 @@ enum class RenderItemStatusIcon { SIMULATION_OWNER = 3, HAS_ACTIONS = 4, OTHER_SIMULATION_OWNER = 5, + CLIENT_ONLY = 6, NONE = 255 }; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index a537ecd0f3..7b51283bac 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -48,13 +48,6 @@ RenderableModelEntityItem::~RenderableModelEntityItem() { void RenderableModelEntityItem::setModelURL(const QString& url) { auto& currentURL = getParsedModelURL(); - if (_model && (currentURL != url)) { - // The machinery for updateModelBounds will give existing models the opportunity to fix their translation/rotation/scale/registration. - // The first two are straightforward, but the latter two have guards to make sure they don't happen after they've already been set. - // Here we reset those guards. This doesn't cause the entity values to change -- it just allows the model to match once it comes in. - _model->setScaleToFit(false, getDimensions()); - _model->setSnapModelToRegistrationPoint(false, getRegistrationPoint()); - } ModelEntityItem::setModelURL(url); if (currentURL != getParsedModelURL() || !_model) { @@ -163,6 +156,14 @@ void RenderableModelEntityItem::remapTextures() { } void RenderableModelEntityItem::doInitialModelSimulation() { + // The machinery for updateModelBounds will give existing models the opportunity to fix their + // translation/rotation/scale/registration. The first two are straightforward, but the latter two have guards to + // make sure they don't happen after they've already been set. Here we reset those guards. This doesn't cause the + // entity values to change -- it just allows the model to match once it comes in. + _model->setScaleToFit(false, getDimensions()); + _model->setSnapModelToRegistrationPoint(false, getRegistrationPoint()); + + // now recalculate the bounds and registration _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(getRotation()); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 1d5570b8b7..ad35a1a00c 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -981,7 +981,7 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() { PhysicalEntitySimulationPointer peSimulation = std::static_pointer_cast(simulation); EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr; if (packetSender) { - packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties); + packetSender->queueEditEntityMessage(PacketType::EntityEdit, tree, entity->getID(), properties); } }); }); diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 1e38c32964..b96911c8c2 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include #include @@ -24,9 +25,7 @@ EntityEditPacketSender::EntityEditPacketSender() { } void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer message, SharedNodePointer sendingNode) { - if (_shouldProcessNack) { - processNackPacket(*message, sendingNode); - } + processNackPacket(*message, sendingNode); } void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, qint64 clockSkew) { @@ -35,18 +34,76 @@ void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, QByte } } -void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemID modelID, - const EntityItemProperties& properties) { +void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, + EntityTreePointer entityTree, + EntityItemID entityItemID, + const EntityItemProperties& properties) { if (!_shouldSend) { return; // bail early } + if (properties.getOwningAvatarID() != _myAvatar->getID()) { + return; // don't send updates for someone else's avatarEntity + } + + assert(properties.getClientOnly()); + + // this is an avatar-based entity. update our avatar-data rather than sending to the entity-server + assert(_myAvatar); + + if (!entityTree) { + qDebug() << "EntityEditPacketSender::queueEditEntityMessage null entityTree."; + return; + } + EntityItemPointer entity = entityTree->findEntityByEntityItemID(entityItemID); + if (!entity) { + qDebug() << "EntityEditPacketSender::queueEditEntityMessage can't find entity."; + return; + } + + // the properties that get serialized into the avatar identity packet should be the entire set + // rather than just the ones being edited. + entity->setProperties(properties); + EntityItemProperties entityProperties = entity->getProperties(); + + QScriptValue scriptProperties = EntityItemNonDefaultPropertiesToScriptValue(&_scriptEngine, entityProperties); + QVariant variantProperties = scriptProperties.toVariant(); + QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); + + // the ID of the parent/avatar changes from session to session. use a special UUID to indicate the avatar + QJsonObject jsonObject = jsonProperties.object(); + if (QUuid(jsonObject["parentID"].toString()) == _myAvatar->getID()) { + jsonObject["parentID"] = AVATAR_SELF_ID.toString(); + } + jsonProperties = QJsonDocument(jsonObject); + + QByteArray binaryProperties = jsonProperties.toBinaryData(); + _myAvatar->updateAvatarEntity(entityItemID, binaryProperties); + + entity->setLastBroadcast(usecTimestampNow()); + return; +} + + +void EntityEditPacketSender::queueEditEntityMessage(PacketType type, + EntityTreePointer entityTree, + EntityItemID entityItemID, + const EntityItemProperties& properties) { + if (!_shouldSend) { + return; // bail early + } + + if (properties.getClientOnly()) { + queueEditAvatarEntityMessage(type, entityTree, entityItemID, properties); + return; + } + QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0); - if (EntityItemProperties::encodeEntityEditPacket(type, modelID, properties, bufferOut)) { + if (EntityItemProperties::encodeEntityEditPacket(type, entityItemID, properties, bufferOut)) { #ifdef WANT_DEBUG qCDebug(entities) << "calling queueOctreeEditMessage()..."; - qCDebug(entities) << " id:" << modelID; + qCDebug(entities) << " id:" << entityItemID; qCDebug(entities) << " properties:" << properties; #endif queueOctreeEditMessage(type, bufferOut); @@ -58,6 +115,10 @@ void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityI return; // bail early } + // in case this was a clientOnly entity: + assert(_myAvatar); + _myAvatar->clearAvatarEntity(entityItemID); + QByteArray bufferOut(NLPacket::maxPayloadSize(PacketType::EntityErase), 0); if (EntityItemProperties::encodeEraseEntityMessage(entityItemID, bufferOut)) { diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 26e4dd83ff..1991142f3f 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -15,6 +15,7 @@ #include #include "EntityItem.h" +#include "AvatarData.h" /// Utility for processing, packing, queueing and sending of outbound edit voxel messages. class EntityEditPacketSender : public OctreeEditPacketSender { @@ -22,11 +23,21 @@ class EntityEditPacketSender : public OctreeEditPacketSender { public: EntityEditPacketSender(); + void setMyAvatar(AvatarData* myAvatar) { _myAvatar = myAvatar; } + AvatarData* getMyAvatar() { return _myAvatar; } + void clearAvatarEntity(QUuid entityID) { assert(_myAvatar); _myAvatar->clearAvatarEntity(entityID); } + + void queueEditAvatarEntityMessage(PacketType type, EntityTreePointer entityTree, + EntityItemID entityItemID, const EntityItemProperties& properties); + + /// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines /// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in /// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known. /// NOTE: EntityItemProperties assumes that all distances are in meter units - void queueEditEntityMessage(PacketType type, EntityItemID modelID, const EntityItemProperties& properties); + void queueEditEntityMessage(PacketType type, EntityTreePointer entityTree, + EntityItemID entityItemID, const EntityItemProperties& properties); + void queueEraseEntityMessage(const EntityItemID& entityItemID); @@ -36,9 +47,9 @@ public: public slots: void processEntityEditNackPacket(QSharedPointer message, SharedNodePointer sendingNode); - void toggleNackPackets() { _shouldProcessNack = !_shouldProcessNack; } private: - bool _shouldProcessNack = true; + AvatarData* _myAvatar { nullptr }; + QScriptEngine _scriptEngine; }; #endif // hifi_EntityEditPacketSender_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index bd4292f75d..dc5fbf7d8e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -137,6 +137,9 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_PARENT_JOINT_INDEX; requestedProperties += PROP_QUERY_AA_CUBE; + requestedProperties += PROP_CLIENT_ONLY; + requestedProperties += PROP_OWNING_AVATAR_ID; + return requestedProperties; } @@ -1093,6 +1096,8 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper properties._id = getID(); properties._idSet = true; properties._created = _created; + properties.setClientOnly(_clientOnly); + properties.setOwningAvatarID(_owningAvatarID); properties._type = getType(); @@ -1133,6 +1138,9 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper COPY_ENTITY_PROPERTY_TO_PROPERTIES(localPosition, getLocalPosition); COPY_ENTITY_PROPERTY_TO_PROPERTIES(localRotation, getLocalOrientation); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(clientOnly, getClientOnly); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(owningAvatarID, getOwningAvatarID); + properties._defaultSettings = false; return properties; @@ -1222,6 +1230,9 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex); SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(clientOnly, setClientOnly); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(owningAvatarID, setOwningAvatarID); + AACube saveQueryAACube = _queryAACube; checkAndAdjustQueryAACube(); if (saveQueryAACube != _queryAACube) { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 4d3d93e40f..2c6dc9b74d 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -372,6 +372,7 @@ public: glm::vec3 entityToWorld(const glm::vec3& point) const; quint64 getLastEditedFromRemote() const { return _lastEditedFromRemote; } + void updateLastEditedFromRemote() { _lastEditedFromRemote = usecTimestampNow(); } void getAllTerseUpdateProperties(EntityItemProperties& properties) const; @@ -422,12 +423,19 @@ public: /// entity to definitively state if the preload signal should be sent. /// /// We only want to preload if: - /// there is some script, and either the script value or the scriptTimestamp + /// there is some script, and either the script value or the scriptTimestamp /// value have changed since our last preload - bool shouldPreloadScript() const { return !_script.isEmpty() && + bool shouldPreloadScript() const { return !_script.isEmpty() && ((_loadedScript != _script) || (_loadedScriptTimestamp != _scriptTimestamp)); } void scriptHasPreloaded() { _loadedScript = _script; _loadedScriptTimestamp = _scriptTimestamp; } + bool getClientOnly() const { return _clientOnly; } + void setClientOnly(bool clientOnly) { _clientOnly = clientOnly; } + // if this entity is client-only, which avatar is it associated with? + QUuid getOwningAvatarID() const { return _owningAvatarID; } + void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } + + protected: void setSimulated(bool simulated) { _simulated = simulated; } @@ -539,6 +547,9 @@ protected: mutable QHash _previouslyDeletedActions; QUuid _sourceUUID; /// the server node UUID we came from + + bool _clientOnly { false }; + QUuid _owningAvatarID; }; #endif // hifi_EntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7893fe4a37..99285b4986 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -308,6 +308,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_LOCAL_POSITION, localPosition); CHECK_PROPERTY_CHANGE(PROP_LOCAL_ROTATION, localRotation); + CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed); + CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed); + + CHECK_PROPERTY_CHANGE(PROP_CLIENT_ONLY, clientOnly); + CHECK_PROPERTY_CHANGE(PROP_OWNING_AVATAR_ID, owningAvatarID); + changedProperties += _animation.getChangedProperties(); changedProperties += _keyLight.getChangedProperties(); changedProperties += _skybox.getChangedProperties(); @@ -465,6 +471,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool _stage.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); _skybox.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GHOSTING_ALLOWED, ghostingAllowed); } // Web only @@ -539,6 +548,12 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); + + properties.setProperty("clientOnly", convertScriptValue(engine, getClientOnly())); + properties.setProperty("owningAvatarID", convertScriptValue(engine, getOwningAvatarID())); + // FIXME - I don't think these properties are supported any more //COPY_PROPERTY_TO_QSCRIPTVALUE(localRenderAlpha); @@ -675,6 +690,12 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslationsSet, qVectorBool, setJointTranslationsSet); COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslations, qVectorVec3, setJointTranslations); + COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed); + COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed); + + COPY_PROPERTY_FROM_QSCRIPTVALUE(clientOnly, bool, setClientOnly); + COPY_PROPERTY_FROM_QSCRIPTVALUE(owningAvatarID, QUuid, setOwningAvatarID); + _lastEdited = usecTimestampNow(); } @@ -845,6 +866,9 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_HOUR, Stage, stage, Hour, hour); ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_AUTOMATIC_HOURDAY, Stage, stage, AutomaticHourDay, automaticHourDay); + ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool); + ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool); + // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -1085,6 +1109,9 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem _staticSkybox.setProperties(properties); _staticSkybox.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + + APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, properties.getFlyingAllowed()); + APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, properties.getGhostingAllowed()); } if (properties.getType() == EntityTypes::PolyVox) { @@ -1369,6 +1396,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode); properties.getSkybox().decodeFromEditPacket(propertyFlags, dataAt , processedBytes); + + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); } if (properties.getType() == EntityTypes::PolyVox) { @@ -1559,6 +1589,12 @@ void EntityItemProperties::markAllChanged() { _jointTranslationsChanged = true; _queryAACubeChanged = true; + + _flyingAllowedChanged = true; + _ghostingAllowedChanged = true; + + _clientOnlyChanged = true; + _owningAvatarIDChanged = true; } // The minimum bounding box for the entity. @@ -1879,6 +1915,19 @@ QList EntityItemProperties::listChangedProperties() { if (queryAACubeChanged()) { out += "queryAACube"; } + if (clientOnlyChanged()) { + out += "clientOnly"; + } + if (owningAvatarIDChanged()) { + out += "owningAvatarID"; + } + + if (flyingAllowedChanged()) { + out += "flyingAllowed"; + } + if (ghostingAllowedChanged()) { + out += "ghostingAllowed"; + } getAnimation().listChangedProperties(out); getKeyLight().listChangedProperties(out); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 49981913b5..59749cfef5 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -205,6 +205,12 @@ public: DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector, QVector()); DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, QVector()); + DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED); + DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); + + DEFINE_PROPERTY(PROP_CLIENT_ONLY, ClientOnly, clientOnly, bool, false); + DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID); + static QString getBackgroundModeString(BackgroundMode mode); @@ -416,6 +422,12 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, JointTranslationsSet, jointTranslationsSet, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, JointTranslations, jointTranslations, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, FlyingAllowed, flyingAllowed, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, GhostingAllowed, ghostingAllowed, ""); + + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ClientOnly, clientOnly, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, ""); + properties.getAnimation().debugDump(); properties.getSkybox().debugDump(); properties.getStage().debugDump(); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 90a7c1e2f7..20c451eaae 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -169,6 +169,12 @@ enum EntityPropertyList { PROP_FALLOFF_RADIUS, // for Light entity + PROP_FLYING_ALLOWED, // can avatars in a zone fly? + PROP_GHOSTING_ALLOWED, // can avatars in a zone turn off physics? + + PROP_CLIENT_ONLY, // doesn't go over wire + PROP_OWNING_AVATAR_ID, // doesn't go over wire + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 01f444491d..15c2bffd80 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -36,7 +36,7 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership void EntityScriptingInterface::queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties) { - getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties); + getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties); } bool EntityScriptingInterface::canAdjustLocks() { @@ -123,10 +123,17 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti } -QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) { +QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) { EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); + if (clientOnly) { + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + propertiesWithSimID.setClientOnly(clientOnly); + propertiesWithSimID.setOwningAvatarID(myNodeID); + } + auto dimensions = propertiesWithSimID.getDimensions(); float volume = dimensions.x * dimensions.y * dimensions.z; auto density = propertiesWithSimID.getDensity(); @@ -272,13 +279,21 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& bool updatedEntity = false; _entityTree->withWriteLock([&] { + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); + if (!entity) { + return; + } + + auto nodeList = DependencyManager::get(); + if (entity->getClientOnly() && entity->getOwningAvatarID() != nodeList->getSessionUUID()) { + // don't edit other avatar's avatarEntities + return; + } + if (scriptSideProperties.parentRelatedPropertyChanged()) { // All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them. // If any of these changed, pull any missing properties from the entity. - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); - if (!entity) { - return; - } + //existing entity, retrieve old velocity for check down below oldVelocity = entity->getVelocity().length(); @@ -296,6 +311,8 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } } properties = convertLocationFromScriptSemantics(properties); + properties.setClientOnly(entity->getClientOnly()); + properties.setOwningAvatarID(entity->getOwningAvatarID()); float cost = calculateCost(density * volume, oldVelocity, newVelocity); cost *= costMultiplier; @@ -384,6 +401,14 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); if (entity) { + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + if (entity->getClientOnly() && entity->getOwningAvatarID() != myNodeID) { + // don't delete other avatar's avatarEntities + shouldDelete = false; + return; + } + auto dimensions = entity->getDimensions(); float volume = dimensions.x * dimensions.y * dimensions.z; auto density = entity->getDensity(); @@ -771,6 +796,11 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, return false; } + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + + EntityItemProperties properties; + EntityItemPointer entity; bool doTransmit = false; _entityTree->withWriteLock([&] { @@ -786,15 +816,20 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, return; } + if (entity->getClientOnly() && entity->getOwningAvatarID() != myNodeID) { + return; + } + doTransmit = actor(simulation, entity); if (doTransmit) { + properties.setClientOnly(entity->getClientOnly()); + properties.setOwningAvatarID(entity->getOwningAvatarID()); _entityTree->entityChanged(entity); } }); // transmit the change if (doTransmit) { - EntityItemProperties properties; _entityTree->withReadLock([&] { properties = entity->getProperties(); }); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 5f80b7abb2..2f5446874b 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -82,7 +82,7 @@ public slots: Q_INVOKABLE bool canRez(); /// adds a model with the specific properties - Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties); + Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool clientOnly = false); /// temporary method until addEntity can be used from QJSEngine Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const glm::vec3& position); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 97d4e91acd..b85ee1dcf9 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -311,7 +311,9 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer result = NULL; - if (getIsClient()) { + bool clientOnly = properties.getClientOnly(); + + if (!clientOnly && getIsClient()) { // if our Node isn't allowed to create entities in this domain, don't try. auto nodeList = DependencyManager::get(); if (nodeList && !nodeList->getThisNodeCanRez()) { @@ -1382,8 +1384,11 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + EntityTreePointer tree = entityTreeElement->getTree(); + // queue the packet to send to the server - args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, newID, properties); + args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, tree, newID, properties); // also update the local tree instantly (note: this is not our tree, but an alternate tree) if (args->otherTree) { diff --git a/libraries/entities/src/SimulationOwner.h b/libraries/entities/src/SimulationOwner.h index 1afec426d7..5f940bbe25 100644 --- a/libraries/entities/src/SimulationOwner.h +++ b/libraries/entities/src/SimulationOwner.h @@ -27,6 +27,7 @@ const quint8 RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1; // When poking objects with scripts an observer will bid at SCRIPT_EDIT priority. const quint8 SCRIPT_GRAB_SIMULATION_PRIORITY = 0x80; const quint8 SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1; +const quint8 AVATAR_ENTITY_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY + 1; // PERSONAL priority (needs a better name) is the level at which a simulation observer owns its own avatar // which really just means: things that collide with it will be bid at a priority level one lower diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 6aabef5cc0..a28b8210c2 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -23,8 +23,12 @@ bool ZoneEntityItem::_zonesArePickable = false; bool ZoneEntityItem::_drawZoneBoundaries = false; + const ShapeType ZoneEntityItem::DEFAULT_SHAPE_TYPE = SHAPE_TYPE_BOX; const QString ZoneEntityItem::DEFAULT_COMPOUND_SHAPE_URL = ""; +const bool ZoneEntityItem::DEFAULT_FLYING_ALLOWED = true; +const bool ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED = true; + EntityItemPointer ZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity { new ZoneEntityItem(entityID) }; @@ -55,6 +59,9 @@ EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredPr _skyboxProperties.getProperties(properties); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed); + return properties; } @@ -70,6 +77,9 @@ bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundMode, setBackgroundMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); + bool somethingChangedInSkybox = _skyboxProperties.setProperties(properties); somethingChanged = somethingChanged || somethingChangedInKeyLight || somethingChangedInStage || somethingChangedInSkybox; @@ -116,6 +126,9 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, bytesRead += bytesFromSkybox; dataAt += bytesFromSkybox; + READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); + READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); + return bytesRead; } @@ -125,13 +138,16 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); requestedProperties += _keyLightProperties.getEntityProperties(params); - + requestedProperties += PROP_SHAPE_TYPE; requestedProperties += PROP_COMPOUND_SHAPE_URL; requestedProperties += PROP_BACKGROUND_MODE; requestedProperties += _stageProperties.getEntityProperties(params); requestedProperties += _skyboxProperties.getEntityProperties(params); - + + requestedProperties += PROP_FLYING_ALLOWED; + requestedProperties += PROP_GHOSTING_ALLOWED; + return requestedProperties; } @@ -155,10 +171,12 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, (uint32_t)getBackgroundMode()); // could this be a uint16?? - + _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed()); + APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed()); } void ZoneEntityItem::debugDump() const { diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 0326a0f441..56968aa9c9 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -70,6 +70,11 @@ public: const SkyboxPropertyGroup& getSkyboxProperties() const { return _skyboxProperties; } const StagePropertyGroup& getStageProperties() const { return _stageProperties; } + bool getFlyingAllowed() const { return _flyingAllowed; } + void setFlyingAllowed(bool value) { _flyingAllowed = value; } + bool getGhostingAllowed() const { return _ghostingAllowed; } + void setGhostingAllowed(bool value) { _ghostingAllowed = value; } + virtual bool supportsDetailedRayIntersection() const { return true; } virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, @@ -80,18 +85,23 @@ public: static const ShapeType DEFAULT_SHAPE_TYPE; static const QString DEFAULT_COMPOUND_SHAPE_URL; - + static const bool DEFAULT_FLYING_ALLOWED; + static const bool DEFAULT_GHOSTING_ALLOWED; + protected: KeyLightPropertyGroup _keyLightProperties; - + ShapeType _shapeType = DEFAULT_SHAPE_TYPE; QString _compoundShapeURL; - + BackgroundMode _backgroundMode = BACKGROUND_MODE_INHERIT; StagePropertyGroup _stageProperties; SkyboxPropertyGroup _skyboxProperties; + bool _flyingAllowed { DEFAULT_FLYING_ALLOWED }; + bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED }; + static bool _drawZoneBoundaries; static bool _zonesArePickable; }; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp index e56d671891..ac337550ca 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendBuffer.cpp @@ -25,7 +25,7 @@ public: glBufferData(GL_ARRAY_BUFFER, _size, nullptr, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); - if (original) { + if (original && original->_size) { glBindBuffer(GL_COPY_WRITE_BUFFER, _buffer); glBindBuffer(GL_COPY_READ_BUFFER, original->_buffer); glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, original->_size); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 4c0240eaf7..915ec1db87 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -20,11 +20,10 @@ const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; -void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { - +void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { auto userInputMapper = DependencyManager::get(); userInputMapper->withLock([&, this]() { - _inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured); + _inputDevice->update(deltaTime, inputCalibrationData); }); // For touch event, we need to check that the last event is not too long ago @@ -40,7 +39,7 @@ void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputC } } -void KeyboardMouseDevice::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void KeyboardMouseDevice::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _axisStateMap.clear(); } diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index 67d9ccb1b2..a66cc7060b 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -65,12 +65,11 @@ public: }; // Plugin functions - virtual bool isSupported() const override { return true; } - virtual bool isJointController() const override { return false; } - virtual const QString& getName() const override { return NAME; } + bool isSupported() const override { return true; } + const QString& getName() const override { return NAME; } - virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void keyPressEvent(QKeyEvent* event); void keyReleaseEvent(QKeyEvent* event); @@ -97,7 +96,7 @@ protected: // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void focusOutEvent() override; // Let's make it easy for Qt because we assume we love Qt forever diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index a97f4df35d..619a1d7903 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -144,12 +144,21 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { // 4. domain network address (IP or dns resolvable hostname) // use our regex'ed helpers to figure out what we're supposed to do with this - if (!handleUsername(lookupUrl.authority())) { + if (handleUsername(lookupUrl.authority())) { + // handled a username for lookup + + // in case we're failing to connect to where we thought this user was + // store their username as previous lookup so we can refresh their location via API + _previousLookup = lookupUrl; + } else { // we're assuming this is either a network address or global place name // check if it is a network address first bool hostChanged; if (handleNetworkAddress(lookupUrl.host() - + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())), trigger, hostChanged)) { + + (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())), trigger, hostChanged)) { + + // a network address lookup clears the previous lookup since we don't expect to re-attempt it + _previousLookup.clear(); // If the host changed then we have already saved to history if (hostChanged) { @@ -165,10 +174,16 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { // we may have a path that defines a relative viewpoint - if so we should jump to that now handlePath(path, trigger); } else if (handleDomainID(lookupUrl.host())){ + // store this domain ID as the previous lookup in case we're failing to connect and want to refresh API info + _previousLookup = lookupUrl; + // no place name - this is probably a domain ID // try to look up the domain ID on the metaverse API attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger); } else { + // store this place name as the previous lookup in case we fail to connect and want to refresh API info + _previousLookup = lookupUrl; + // wasn't an address - lookup the place name // we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path(), trigger); @@ -180,9 +195,13 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) { } else if (lookupUrl.toString().startsWith('/')) { qCDebug(networking) << "Going to relative path" << lookupUrl.path(); + // a path lookup clears the previous lookup since we don't expect to re-attempt it + _previousLookup.clear(); + // if this is a relative path then handle it as a relative viewpoint handlePath(lookupUrl.path(), trigger, true); emit lookupResultsFinished(); + return true; } @@ -276,7 +295,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const qCDebug(networking) << "Possible domain change required to connect to" << domainHostname << "on" << domainPort; - emit possibleDomainChangeRequired(domainHostname, domainPort); + emit possibleDomainChangeRequired(domainHostname, domainPort, domainID); } else { QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString(); @@ -295,21 +314,30 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const // set our current root place name to the name that came back const QString PLACE_NAME_KEY = "name"; QString placeName = rootMap[PLACE_NAME_KEY].toString(); + if (!placeName.isEmpty()) { if (setHost(placeName, trigger)) { trigger = LookupTrigger::Internal; } + + _placeName = placeName; } else { if (setHost(domainIDString, trigger)) { trigger = LookupTrigger::Internal; } + + // this isn't a place, so clear the place name + _placeName.clear(); } // check if we had a path to override the path returned QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString(); if (!overridePath.isEmpty()) { - handlePath(overridePath, trigger); + // make sure we don't re-handle an overriden path if this was a refresh of info from API + if (trigger != LookupTrigger::AttemptedRefresh) { + handlePath(overridePath, trigger); + } } else { // take the path that came back const QString PLACE_PATH_KEY = "path"; @@ -580,13 +608,15 @@ bool AddressManager::setHost(const QString& host, LookupTrigger trigger, quint16 bool AddressManager::setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger) { bool hostChanged = setHost(hostname, trigger, port); + // clear any current place information _rootPlaceID = QUuid(); + _placeName.clear(); qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port; DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); - emit possibleDomainChangeRequired(hostname, port); + emit possibleDomainChangeRequired(hostname, port, QUuid()); return hostChanged; } @@ -606,6 +636,13 @@ void AddressManager::goToUser(const QString& username) { QByteArray(), nullptr, requestParams); } +void AddressManager::refreshPreviousLookup() { + // if we have a non-empty previous lookup, fire it again now (but don't re-store it in the history) + if (!_previousLookup.isEmpty()) { + handleUrl(_previousLookup, LookupTrigger::AttemptedRefresh); + } +} + void AddressManager::copyAddress() { QApplication::clipboard()->setText(currentAddress().toString()); } @@ -617,7 +654,10 @@ void AddressManager::copyPath() { void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) { // if we're cold starting and this is called for the first address (from settings) we don't do anything - if (trigger != LookupTrigger::StartupFromSettings && trigger != LookupTrigger::DomainPathResponse) { + if (trigger != LookupTrigger::StartupFromSettings + && trigger != LookupTrigger::DomainPathResponse + && trigger != LookupTrigger::AttemptedRefresh) { + if (trigger == LookupTrigger::Back) { // we're about to push to the forward stack // if it's currently empty emit our signal to say that going forward is now possible diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index dd0dbd9f38..a3aaee3ba2 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -48,7 +48,8 @@ public: Forward, StartupFromSettings, DomainPathResponse, - Internal + Internal, + AttemptedRefresh }; bool isConnected(); @@ -58,6 +59,7 @@ public: const QString currentPath(bool withOrientation = true) const; const QUuid& getRootPlaceID() const { return _rootPlaceID; } + const QString& getPlaceName() const { return _placeName; } const QString& getHost() const { return _host; } @@ -88,6 +90,8 @@ public slots: void goToUser(const QString& username); + void refreshPreviousLookup(); + void storeCurrentAddress(); void copyAddress(); @@ -98,7 +102,7 @@ signals: void lookupResultIsOffline(); void lookupResultIsNotFound(); - void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort); + void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort, const QUuid& domainID); void possibleDomainChangeRequiredViaICEForID(const QString& iceServerHostname, const QUuid& domainID); void locationChangeRequired(const glm::vec3& newPosition, @@ -141,6 +145,7 @@ private: QString _host; quint16 _port; + QString _placeName; QUuid _rootPlaceID; PositionGetter _positionGetter; OrientationGetter _orientationGetter; @@ -150,6 +155,8 @@ private: quint64 _lastBackPush = 0; QString _newHostLookupPath; + + QUrl _previousLookup; }; #endif // hifi_AddressManager_h diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 44ce63e6c6..b3c3a28829 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -28,16 +28,8 @@ DomainHandler::DomainHandler(QObject* parent) : QObject(parent), - _uuid(), _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), - _assignmentUUID(), - _connectionToken(), - _iceDomainID(), - _iceClientID(), - _iceServerSockAddr(), _icePeer(this), - _isConnected(false), - _settingsObject(), _settingsTimer(this) { _sockAddr.setObjectName("DomainServer"); @@ -105,7 +97,7 @@ void DomainHandler::hardReset() { softReset(); qCDebug(networking) << "Hard reset in NodeList DomainHandler."; - _iceDomainID = QUuid(); + _pendingDomainID = QUuid(); _iceServerSockAddr = HifiSockAddr(); _hostname = QString(); _sockAddr.clear(); @@ -140,7 +132,7 @@ void DomainHandler::setUUID(const QUuid& uuid) { } } -void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) { +void DomainHandler::setSocketAndID(const QString& hostname, quint16 port, const QUuid& domainID) { if (hostname != _hostname || _sockAddr.getPort() != port) { // re-set the domain info so that auth information is reloaded @@ -169,6 +161,8 @@ void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) { // grab the port by reading the string after the colon _sockAddr.setPort(port); } + + _pendingDomainID = domainID; } void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id) { @@ -179,7 +173,7 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, // refresh our ICE client UUID to something new _iceClientID = QUuid::createUuid(); - _iceDomainID = id; + _pendingDomainID = id; HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr; replaceableSockAddr->~HifiSockAddr(); @@ -341,7 +335,7 @@ void DomainHandler::processICEResponsePacket(QSharedPointer mes DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSPeerInformation); - if (_icePeer.getUUID() != _iceDomainID) { + if (_icePeer.getUUID() != _pendingDomainID) { qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection."; _icePeer.reset(); } else { diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 03141d8fef..c6269191d2 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -58,8 +58,8 @@ public: const QUuid& getAssignmentUUID() const { return _assignmentUUID; } void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } - - const QUuid& getICEDomainID() const { return _iceDomainID; } + + const QUuid& getPendingDomainID() const { return _pendingDomainID; } const QUuid& getICEClientID() const { return _iceClientID; } @@ -85,7 +85,7 @@ public: void softReset(); public slots: - void setHostnameAndPort(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT); + void setSocketAndID(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT, const QUuid& id = QUuid()); void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id); void processSettingsPacketList(QSharedPointer packetList); @@ -126,11 +126,11 @@ private: HifiSockAddr _sockAddr; QUuid _assignmentUUID; QUuid _connectionToken; - QUuid _iceDomainID; + QUuid _pendingDomainID; // ID of domain being connected to, via ICE or direct connection QUuid _iceClientID; HifiSockAddr _iceServerSockAddr; NetworkPeer _icePeer; - bool _isConnected; + bool _isConnected { false }; QJsonObject _settingsObject; QString _pendingPath; QTimer _settingsTimer; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 482d0366fd..a5fff36be8 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -50,7 +50,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // handle domain change signals from AddressManager connect(addressManager.data(), &AddressManager::possibleDomainChangeRequired, - &_domainHandler, &DomainHandler::setHostnameAndPort); + &_domainHandler, &DomainHandler::setSocketAndID); connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID, &_domainHandler, &DomainHandler::setIceServerHostnameAndID); @@ -314,8 +314,10 @@ void NodeList::sendDomainServerCheckIn() { packetStream << connectUUID; } - // pack our data to send to the domain-server - packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); + // pack our data to send to the domain-server including + // the hostname information (so the domain-server can see which place name we came in on) + packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList() + << DependencyManager::get()->getPlaceName(); if (!_domainHandler.isConnected()) { DataServerAccountInfo& accountInfo = accountManager->getAccountInfo(); @@ -344,6 +346,14 @@ void NodeList::sendDomainServerCheckIn() { // increment the count of un-replied check-ins _numNoReplyDomainCheckIns++; } + + if (!_publicSockAddr.isNull() && !_domainHandler.isConnected() && !_domainHandler.getPendingDomainID().isNull()) { + // if we aren't connected to the domain-server, and we have an ID + // (that we presume belongs to a domain in the HF Metaverse) + // we request connection information for the domain every so often to make sure what we have is up to date + + DependencyManager::get()->refreshPreviousLookup(); + } } void NodeList::handleDSPathQuery(const QString& newPath) { @@ -451,7 +461,7 @@ void NodeList::handleICEConnectionToDomainServer() { LimitedNodeList::sendPeerQueryToIceServer(_domainHandler.getICEServerSockAddr(), _domainHandler.getICEClientID(), - _domainHandler.getICEDomainID()); + _domainHandler.getPendingDomainID()); } } @@ -464,7 +474,7 @@ void NodeList::pingPunchForDomainServer() { if (_domainHandler.getICEPeer().getConnectionAttempts() == 0) { qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID" - << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID()); + << uuidStringWithoutCurlyBraces(_domainHandler.getPendingDomainID()); } else { if (_domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) { // if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index e4aab94090..b04d582d6d 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -47,10 +47,10 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityAdd: case PacketType::EntityEdit: case PacketType::EntityData: - return VERSION_LIGHT_HAS_FALLOFF_RADIUS; + return VERSION_ENTITIES_NO_FLY_ZONES; case PacketType::AvatarData: case PacketType::BulkAvatarData: - return static_cast(AvatarMixerPacketVersion::SoftAttachmentSupport); + return static_cast(AvatarMixerPacketVersion::AvatarEntities); case PacketType::ICEServerHeartbeat: return 18; // ICE Server Heartbeat signing case PacketType::AssetGetInfo: @@ -58,6 +58,9 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AssetUpload: // Removal of extension from Asset requests return 18; + case PacketType::DomainConnectRequest: + // addition of referring hostname information + return 18; default: return 17; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index b98a87e439..030b4af8c9 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -171,10 +171,12 @@ const PacketVersion VERSION_ENTITITES_HAVE_QUERY_BOX = 54; const PacketVersion VERSION_ENTITITES_HAVE_COLLISION_MASK = 55; const PacketVersion VERSION_ATMOSPHERE_REMOVED = 56; const PacketVersion VERSION_LIGHT_HAS_FALLOFF_RADIUS = 57; +const PacketVersion VERSION_ENTITIES_NO_FLY_ZONES = 58; enum class AvatarMixerPacketVersion : PacketVersion { TranslationSupport = 17, - SoftAttachmentSupport + SoftAttachmentSupport, + AvatarEntities }; #endif // hifi_PacketHeaders_h diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 9ef059bb05..1c8a9b50b5 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -264,6 +264,10 @@ void CharacterController::setState(State desiredState, const char* reason) { #else void CharacterController::setState(State desiredState) { #endif + if (!_flyingAllowed && desiredState == State::Hover) { + desiredState = State::InAir; + } + if (desiredState != _state) { #ifdef DEBUG_STATE_CHANGE qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason; @@ -644,3 +648,13 @@ bool CharacterController::getRigidBodyLocation(glm::vec3& avatarRigidBodyPositio avatarRigidBodyRotation = bulletToGLM(worldTrans.getRotation()); return true; } + +void CharacterController::setFlyingAllowed(bool value) { + if (_flyingAllowed != value) { + _flyingAllowed = value; + + if (!_flyingAllowed && _state == State::Hover) { + SET_STATE(State::InAir, "flying not allowed"); + } + } +} diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 0d73289cb0..2191f46d55 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -110,6 +110,9 @@ public: bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); + void setFlyingAllowed(bool value); + + protected: #ifdef DEBUG_STATE_CHANGE void setState(State state, const char* reason); @@ -172,6 +175,8 @@ protected: btRigidBody* _rigidBody { nullptr }; uint32_t _pendingFlags { 0 }; uint32_t _previousFlags { 0 }; + + bool _flyingAllowed { true }; }; #endif // hifi_CharacterControllerInterface_h diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index f0539110d3..053bfcbd85 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -404,6 +404,11 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { assert(_body); assert(entityTreeIsLocked()); + if (_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()) { + // don't send updates for someone else's avatarEntities + return false; + } + if (_entity->actionDataNeedsTransmit()) { return true; } @@ -547,8 +552,14 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ qCDebug(physics) << "EntityMotionState::sendUpdate()... calling queueEditEntityMessage()..."; #endif - entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, id, properties); - _entity->setLastBroadcast(usecTimestampNow()); + EntityTreeElementPointer element = _entity->getElement(); + EntityTreePointer tree = element ? element->getTree() : nullptr; + + properties.setClientOnly(_entity->getClientOnly()); + properties.setOwningAvatarID(_entity->getOwningAvatarID()); + + entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, tree, id, properties); + _entity->setLastBroadcast(now); // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server // if they've changed. @@ -559,8 +570,13 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ EntityItemProperties newQueryCubeProperties; newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); newQueryCubeProperties.setLastEdited(properties.getLastEdited()); - entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties); - entityDescendant->setLastBroadcast(usecTimestampNow()); + + newQueryCubeProperties.setClientOnly(entityDescendant->getClientOnly()); + newQueryCubeProperties.setOwningAvatarID(entityDescendant->getOwningAvatarID()); + + entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, tree, + descendant->getID(), newQueryCubeProperties); + entityDescendant->setLastBroadcast(now); } } }); diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index 3fbf8ffaf5..6806b3a398 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -217,6 +217,12 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re } else if (entity->isReadyToComputeShape()) { ShapeInfo shapeInfo; entity->computeShapeInfo(shapeInfo); + int numPoints = shapeInfo.getMaxNumPoints(); + if (numPoints > MAX_HULL_POINTS) { + qWarning() << "convex hull with" << numPoints + << "points for entity" << entity->getName() + << "at" << entity->getPosition() << " will be reduced"; + } btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); if (shape) { EntityMotionState* motionState = new EntityMotionState(shape, entity); diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index de3e9cc794..71b919b7ee 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -10,6 +10,7 @@ // #include +#include #include // for MILLIMETERS_PER_METER @@ -64,6 +65,22 @@ btConvexHullShape* ShapeFactory::createConvexHull(const QVector& poin correctedPoint = (points[i] - center) * relativeScale + center; hull->addPoint(btVector3(correctedPoint[0], correctedPoint[1], correctedPoint[2]), false); } + + if (points.size() > MAX_HULL_POINTS) { + // create hull approximation + btShapeHull shapeHull(hull); + shapeHull.buildHull(margin); + // we cannot copy Bullet shapes so we must create a new one... + btConvexHullShape* newHull = new btConvexHullShape(); + const btVector3* newPoints = shapeHull.getVertexPointer(); + for (int i = 0; i < shapeHull.numVertices(); ++i) { + newHull->addPoint(newPoints[i], false); + } + // ...and delete the old one + delete hull; + hull = newHull; + } + hull->recalcLocalAabb(); return hull; } diff --git a/libraries/plugins/src/plugins/InputPlugin.h b/libraries/plugins/src/plugins/InputPlugin.h index b45fa862c1..02ae5f58d5 100644 --- a/libraries/plugins/src/plugins/InputPlugin.h +++ b/libraries/plugins/src/plugins/InputPlugin.h @@ -18,10 +18,8 @@ namespace controller { class InputPlugin : public Plugin { public: - virtual bool isJointController() const = 0; - virtual void pluginFocusOutEvent() = 0; - virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) = 0; + virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) = 0; }; diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index cedf76b37a..781b508bc7 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -100,10 +100,6 @@ bool Procedural::parseUrl(const QUrl& shaderUrl) { return false; } - if (_shaderUrl == shaderUrl) { - return true; - } - _shaderUrl = shaderUrl; _shaderDirty = true; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 1d9ce65581..0a562de3ba 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -149,17 +149,12 @@ void DeferredLightingEffect::prepare(RenderArgs* args) { batch.setFramebuffer(deferredFbo); - // Clear Color, Depth and Stencil + // Clear Color, Depth and Stencil for deferred buffer batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | + gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_COLOR1 | gpu::Framebuffer::BUFFER_COLOR2 | gpu::Framebuffer::BUFFER_DEPTH | gpu::Framebuffer::BUFFER_STENCIL, - vec4(vec3(0), 1), 1.0, 0.0, true); - - // clear the normal and specular buffers - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR1, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), true); - const float MAX_SPECULAR_EXPONENT = 128.0f; - batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR2, glm::vec4(0.0f, 0.0f, 0.0f, 1.0f / MAX_SPECULAR_EXPONENT), true); + vec4(vec3(0), 0), 1.0, 0.0, true); }); } @@ -469,9 +464,10 @@ void DeferredLightingEffect::render(const render::RenderContextPointer& renderCo batch.setInputBuffer(0, mesh->getVertexBuffer()); batch.setInputFormat(mesh->getVertexFormat()); - auto& part = mesh->getPartBuffer().get(); - - batch.drawIndexed(model::Mesh::topologyToPrimitive(part._topology), part._numIndices, part._startIndex); + { + auto& part = mesh->getPartBuffer().get(0); + batch.drawIndexed(model::Mesh::topologyToPrimitive(part._topology), part._numIndices, part._startIndex); + } } } } @@ -548,14 +544,12 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo // Stencil test all the light passes for objects pixels only, not the background state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); - + if (lightVolume) { state->setCullMode(gpu::State::CULL_BACK); - - // No need for z test since the depth buffer is not bound state->setDepthTest(true, false, gpu::LESS_EQUAL); - // TODO: We should bind the true depth buffer both as RT and texture for the depth test + state->setDepthTest(true, false, gpu::LESS_EQUAL); + // TODO: We should use DepthClamp and avoid changing geometry for inside /outside cases - state->setDepthClampEnable(true); // additive blending state->setBlendFunction(true, gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::ONE); @@ -662,10 +656,13 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { _spotLightMesh->setIndexBuffer(gpu::BufferView(new gpu::Buffer(sizeof(unsigned short) * indices, (gpu::Byte*) indexData), gpu::Element::INDEX_UINT16)); delete[] indexData; - model::Mesh::Part part(0, indices, 0, model::Mesh::TRIANGLES); - //DEBUG: model::Mesh::Part part(0, indices, 0, model::Mesh::LINE_STRIP); - _spotLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(sizeof(part), (gpu::Byte*) &part), gpu::Element::PART_DRAWCALL)); + std::vector parts; + parts.push_back(model::Mesh::Part(0, indices, 0, model::Mesh::TRIANGLES)); + parts.push_back(model::Mesh::Part(0, indices, 0, model::Mesh::LINE_STRIP)); // outline version + + + _spotLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); } return _spotLightMesh; } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index f695e2d04c..5680660122 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -52,17 +52,19 @@ void RenderShadowMap::run(const render::SceneContextPointer& sceneContext, const batch.setFramebuffer(fbo); batch.clearFramebuffer( gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH, - vec4(vec3(1.0, 1.0, 1.0), 1.0), 1.0, 0, true); + vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true); batch.setProjectionTransform(shadow.getProjection()); batch.setViewTransform(shadow.getView()); auto shadowPipeline = _shapePlumber->pickPipeline(args, ShapeKey()); auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, ShapeKey::Builder().withSkinned()); - args->_pipeline = shadowPipeline; - batch.setPipeline(shadowPipeline->pipeline); std::vector skinnedShapeKeys{}; + + // Iterate through all inShapes and render the unskinned + args->_pipeline = shadowPipeline; + batch.setPipeline(shadowPipeline->pipeline); for (auto items : inShapes) { if (items.first.isSkinned()) { skinnedShapeKeys.push_back(items.first); @@ -71,6 +73,7 @@ void RenderShadowMap::run(const render::SceneContextPointer& sceneContext, const } } + // Reiterate to render the skinned args->_pipeline = shadowSkinnedPipeline; batch.setPipeline(shadowSkinnedPipeline->pipeline); for (const auto& key : skinnedShapeKeys) { diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 43a20057cd..7b86b9b660 100755 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -87,7 +87,8 @@ float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { float evalShadowAttenuation(vec4 position) { vec4 shadowTexcoord = evalShadowTexcoord(position); if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 || - shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0) { + shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0 || + shadowTexcoord.z < 0.0 || shadowTexcoord.z > 1.0) { // If a point is not in the map, do not attenuate return 1.0; } diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 3ebd3d53ce..2114289095 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "ScriptCache.h" + #include #include #include @@ -20,7 +22,7 @@ #include #include -#include "ScriptCache.h" +#include "ScriptEngines.h" #include "ScriptEngineLogging.h" ScriptCache::ScriptCache(QObject* parent) { @@ -78,22 +80,25 @@ void ScriptCache::scriptDownloaded() { QList scriptUsers = _scriptUsers.values(url); _scriptUsers.remove(url); - if (req->getResult() == ResourceRequest::Success) { - auto scriptContents = req->getData(); - _scriptCache[url] = scriptContents; - lock.unlock(); - qCDebug(scriptengine) << "Done downloading script at:" << url.toString(); + if (!DependencyManager::get()->isStopped()) { + if (req->getResult() == ResourceRequest::Success) { + auto scriptContents = req->getData(); + _scriptCache[url] = scriptContents; + lock.unlock(); + qCDebug(scriptengine) << "Done downloading script at:" << url.toString(); - foreach(ScriptUser* user, scriptUsers) { - user->scriptContentsAvailable(url, scriptContents); - } - } else { - lock.unlock(); - qCWarning(scriptengine) << "Error loading script from URL " << url; - foreach(ScriptUser* user, scriptUsers) { - user->errorInLoadingScript(url); + foreach(ScriptUser* user, scriptUsers) { + user->scriptContentsAvailable(url, scriptContents); + } + } else { + lock.unlock(); + qCWarning(scriptengine) << "Error loading script from URL " << url; + foreach(ScriptUser* user, scriptUsers) { + user->errorInLoadingScript(url); + } } } + req->deleteLater(); } @@ -162,9 +167,11 @@ void ScriptCache::scriptContentAvailable() { } } - foreach(contentAvailableCallback thisCallback, allCallbacks) { - thisCallback(url.toString(), scriptContent, true, success); - } req->deleteLater(); -} + if (!DependencyManager::get()->isStopped()) { + foreach(contentAvailableCallback thisCallback, allCallbacks) { + thisCallback(url.toString(), scriptContent, true, success); + } + } +} diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 67b87ae5e0..7c3aad877e 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -294,13 +294,6 @@ void ScriptEngine::waitTillDoneRunning() { auto startedWaiting = usecTimestampNow(); while (workerThread->isRunning()) { - // NOTE: This will be called on the main application thread from stopAllScripts. - // The application thread will need to continue to process events, because - // the scripts will likely need to marshall messages across to the main thread, e.g. - // if they access Settings or Menu in any of their shutdown code. So: - // Process events for the main application thread, allowing invokeMethod calls to pass between threads. - QCoreApplication::processEvents(); - // If the final evaluation takes too long, then tell the script engine to stop running auto elapsedUsecs = usecTimestampNow() - startedWaiting; static const auto MAX_SCRIPT_EVALUATION_TIME = USECS_PER_SECOND; @@ -326,6 +319,17 @@ void ScriptEngine::waitTillDoneRunning() { } } + // NOTE: This will be called on the main application thread from stopAllScripts. + // The application thread will need to continue to process events, because + // the scripts will likely need to marshall messages across to the main thread, e.g. + // if they access Settings or Menu in any of their shutdown code. So: + // Process events for the main application thread, allowing invokeMethod calls to pass between threads. + QCoreApplication::processEvents(); + // In some cases (debugging), processEvents may give the thread enough time to shut down, so recheck it. + if (!thread()) { + break; + } + // Avoid a pure busy wait QThread::yieldCurrentThread(); } diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 72bf7d529e..a9c273b2a7 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -68,6 +68,7 @@ public: // Called at shutdown time void shutdownScripting(); + bool isStopped() const { return _isStopped; } signals: void scriptCountChanged(); @@ -86,7 +87,6 @@ protected: void onScriptEngineLoaded(const QString& scriptFilename); void onScriptEngineError(const QString& scriptFilename); void launchScriptEngine(ScriptEngine* engine); - bool isStopped() const { return _isStopped; } QReadWriteLock _scriptEnginesHashLock; QHash _scriptEnginesHash; diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 1d0cd56b86..0974c88e73 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -99,6 +99,18 @@ uint32_t ShapeInfo::getNumSubShapes() const { } return 1; } + +int ShapeInfo::getMaxNumPoints() const { + int numPoints = 0; + for (int i = 0; i < _points.size(); ++i) { + int n = _points[i].size(); + if (n > numPoints) { + numPoints = n; + } + } + return numPoints; +} + float ShapeInfo::computeVolume() const { const float DEFAULT_VOLUME = 1.0f; float volume = DEFAULT_VOLUME; diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 79390b6680..1632d22450 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -22,6 +22,10 @@ const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored +// Bullet has a mesh generation util for convex shapes that we used to +// trim convex hulls with many points down to only 42 points. +const int MAX_HULL_POINTS = 42; + enum ShapeType { SHAPE_TYPE_NONE, SHAPE_TYPE_BOX, @@ -61,6 +65,7 @@ public: void clearPoints () { _points.clear(); } void appendToPoints (const QVector& newPoints) { _points << newPoints; } + int getMaxNumPoints() const; float computeVolume() const; diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp index d4378d82b3..876de2e698 100644 --- a/libraries/shared/src/StreamUtils.cpp +++ b/libraries/shared/src/StreamUtils.cpp @@ -23,7 +23,7 @@ void StreamUtil::dump(std::ostream& s, const QByteArray& buffer) { while (i < buffer.size()) { for(int j = 0; i < buffer.size() && j < row_size; ++j) { char byte = buffer[i]; - s << hex_digits[(byte >> 4) & 0x0f] << hex_digits[byte & 0x0f] << " "; + s << hex_digits[(byte >> 4) & 0x0f] << hex_digits[byte & 0x0f] << ' '; ++i; } s << "\n"; @@ -31,21 +31,21 @@ void StreamUtil::dump(std::ostream& s, const QByteArray& buffer) { } std::ostream& operator<<(std::ostream& s, const glm::vec3& v) { - s << "<" << v.x << " " << v.y << " " << v.z << ">"; + s << '(' << v.x << ' ' << v.y << ' ' << v.z << ')'; return s; } std::ostream& operator<<(std::ostream& s, const glm::quat& q) { - s << "<" << q.x << " " << q.y << " " << q.z << " " << q.w << ">"; + s << '(' << q.x << ' ' << q.y << ' ' << q.z << ' ' << q.w << ')'; return s; } std::ostream& operator<<(std::ostream& s, const glm::mat4& m) { - s << "["; + s << '['; for (int j = 0; j < 4; ++j) { - s << " " << m[0][j] << " " << m[1][j] << " " << m[2][j] << " " << m[3][j] << ";"; + s << ' ' << m[0][j] << ' ' << m[1][j] << ' ' << m[2][j] << ' ' << m[3][j] << ';'; } - s << " ]"; + s << " ]"; return s; } @@ -69,54 +69,37 @@ QDataStream& operator>>(QDataStream& in, glm::quat& quaternion) { #include QDebug& operator<<(QDebug& dbg, const glm::vec2& v) { - dbg.nospace() << "{type='glm::vec2'" - ", x=" << v.x << - ", y=" << v.y << - "}"; + dbg.nospace() << '(' << v.x << ", " << v.y << ')'; return dbg; } QDebug& operator<<(QDebug& dbg, const glm::vec3& v) { - dbg.nospace() << "{type='glm::vec3'" - ", x=" << v.x << - ", y=" << v.y << - ", z=" << v.z << - "}"; + dbg.nospace() << '(' << v.x << ", " << v.y << ", " << v.z << ')'; return dbg; } QDebug& operator<<(QDebug& dbg, const glm::vec4& v) { - dbg.nospace() << "{type='glm::vec4'" - ", x=" << v.x << - ", y=" << v.y << - ", z=" << v.z << - ", w=" << v.w << - "}"; + dbg.nospace() << '(' << v.x << ", " << v.y << ", " << v.z << ", " << v.w << ')'; return dbg; } QDebug& operator<<(QDebug& dbg, const glm::quat& q) { - dbg.nospace() << "{type='glm::quat'" - ", x=" << q.x << - ", y=" << q.y << - ", z=" << q.z << - ", w=" << q.w << - "}"; + dbg.nospace() << '(' << q.x << ", " << q.y << ", " << q.z << ", " << q.w << ')'; return dbg; } QDebug& operator<<(QDebug& dbg, const glm::mat4& m) { - dbg.nospace() << "{type='glm::mat4', ["; + dbg.nospace() << '['; for (int j = 0; j < 4; ++j) { dbg << ' ' << m[0][j] << ' ' << m[1][j] << ' ' << m[2][j] << ' ' << m[3][j] << ';'; } - return dbg << " ]}"; + return dbg << " ]"; } QDebug& operator<<(QDebug& dbg, const QVariantHash& v) { - dbg.nospace() << "["; + dbg.nospace() << "[ "; for (QVariantHash::const_iterator it = v.constBegin(); it != v.constEnd(); it++) { - dbg << it.key() << ":" << it.value(); + dbg << it.key() << ':' << it.value(); } return dbg << " ]"; } diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 4fb25e3e3f..dfd9056703 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -590,6 +590,7 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { // let the parent class do it's work bool result = OffscreenQmlSurface::eventFilter(originalDestination, event); + // Check if this is a key press/release event that might need special attention auto type = event->type(); if (type != QEvent::KeyPress && type != QEvent::KeyRelease) { @@ -597,7 +598,8 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { } QKeyEvent* keyEvent = dynamic_cast(event); - bool& pressed = _pressedKeys[keyEvent->key()]; + auto key = keyEvent->key(); + bool& pressed = _pressedKeys[key]; // Keep track of which key press events the QML has accepted if (result && QEvent::KeyPress == type) { @@ -607,7 +609,7 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { // QML input elements absorb key press, but apparently not key release. // therefore we want to ensure that key release events for key presses that were // accepted by the QML layer are suppressed - if (!result && type == QEvent::KeyRelease && pressed) { + if (type == QEvent::KeyRelease && pressed) { pressed = false; return true; } diff --git a/plugins/hifiNeuron/CMakeLists.txt b/plugins/hifiNeuron/CMakeLists.txt index 1cab2359a9..a9ed8cca6e 100644 --- a/plugins/hifiNeuron/CMakeLists.txt +++ b/plugins/hifiNeuron/CMakeLists.txt @@ -6,8 +6,11 @@ # See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html # -set(TARGET_NAME hifiNeuron) -setup_hifi_plugin(Script Qml Widgets) -link_hifi_libraries(shared controllers ui plugins input-plugins) -target_neuron() +if (APPLE OR WIN32) + + set(TARGET_NAME hifiNeuron) + setup_hifi_plugin(Script Qml Widgets) + link_hifi_libraries(shared controllers ui plugins input-plugins) + target_neuron() +endif() diff --git a/plugins/hifiNeuron/src/NeuronPlugin.cpp b/plugins/hifiNeuron/src/NeuronPlugin.cpp index 6e2f744173..0a4bc7f8d2 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.cpp +++ b/plugins/hifiNeuron/src/NeuronPlugin.cpp @@ -25,9 +25,7 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") #define __OS_XUN__ 1 #define BOOL int -#ifdef HAVE_NEURON #include -#endif const QString NeuronPlugin::NAME = "Neuron"; const QString NeuronPlugin::NEURON_ID_STRING = "Perception Neuron"; @@ -166,69 +164,6 @@ static controller::StandardPoseChannel neuronJointIndexToPoseIndexMap[NeuronJoin static glm::vec3 rightHandThumb1DefaultAbsTranslation(-2.155500650405884, -0.7610001564025879, 2.685631036758423); static glm::vec3 leftHandThumb1DefaultAbsTranslation(2.1555817127227783, -0.7603635787963867, 2.6856393814086914); -// default translations (cm) -static glm::vec3 neuronJointTranslations[NeuronJointIndex::Size] = { - {131.901, 95.6602, -27.9815}, - {-9.55907, -1.58772, 0.0760284}, - {0.0144232, -41.4683, -0.105322}, - {1.59348, -41.5875, -0.557237}, - {9.72077, -1.68926, -0.280643}, - {0.0886684, -43.1586, -0.0111596}, - {-2.98473, -44.0517, 0.0694456}, - {0.110967, 16.3959, 0.140463}, - {0.0500451, 10.0238, 0.0731921}, - {0.061568, 10.4352, 0.0583075}, - {0.0500606, 10.0217, 0.0711083}, - {0.0317731, 10.7176, 0.0779325}, - {-0.0204253, 9.71067, 0.131734}, - {-3.24245, 7.13584, 0.185638}, - {-13.0885, -0.0877601, 0.176065}, - {-27.2674, 0.0688724, 0.0272146}, - {-26.7673, 0.0301916, 0.0102847}, - {-2.56017, 0.195537, 3.20968}, - {-3.78796, 0, 0}, - {-2.63141, 0, 0}, - {-3.31579, 0.522947, 2.03495}, - {-5.36589, -0.0939789, 1.02771}, - {-3.72278, 0, 0}, - {-2.11074, 0, 0}, - {-3.47874, 0.532042, 0.778358}, - {-5.32194, -0.0864, 0.322863}, - {-4.06232, 0, 0}, - {-2.54653, 0, 0}, - {-3.46131, 0.553263, -0.132632}, - {-4.76716, -0.0227368, -0.492632}, - {-3.54073, 0, 0}, - {-2.45634, 0, 0}, - {-3.25137, 0.482779, -1.23613}, - {-4.25937, -0.0227368, -1.12168}, - {-2.83528, 0, 0}, - {-1.79166, 0, 0}, - {3.25624, 7.13148, -0.131575}, - {13.149, -0.052598, -0.125076}, - {27.2903, 0.00282644, -0.0181535}, - {26.6602, 0.000969969, -0.0487599}, - {2.56017, 0.195537, 3.20968}, - {3.78796, 0, 0}, - {2.63141, 0, 0}, - {3.31579, 0.522947, 2.03495}, - {5.36589, -0.0939789, 1.02771}, - {3.72278, 0, 0}, - {2.11074, 0, 0}, - {3.47874, 0.532042, 0.778358}, - {5.32194, -0.0864, 0.322863}, - {4.06232, 0, 0}, - {2.54653, 0, 0}, - {3.46131, 0.553263, -0.132632}, - {4.76716, -0.0227368, -0.492632}, - {3.54073, 0, 0}, - {2.45634, 0, 0}, - {3.25137, 0.482779, -1.23613}, - {4.25937, -0.0227368, -1.12168}, - {2.83528, 0, 0}, - {1.79166, 0, 0} -}; - static controller::StandardPoseChannel neuronJointIndexToPoseIndex(NeuronJointIndex i) { assert(i >= 0 && i < NeuronJointIndex::Size); if (i >= 0 && i < NeuronJointIndex::Size) { @@ -307,16 +242,13 @@ static const char* controllerJointName(controller::StandardPoseChannel i) { // convert between YXZ neuron euler angles in degrees to quaternion // this is the default setting in the Axis Neuron server. -static quat eulerToQuat(vec3 euler) { +static quat eulerToQuat(const vec3& e) { // euler.x and euler.y are swaped, WTF. - glm::vec3 e = glm::vec3(euler.y, euler.x, euler.z) * RADIANS_PER_DEGREE; - return (glm::angleAxis(e.y, Vectors::UNIT_Y) * - glm::angleAxis(e.x, Vectors::UNIT_X) * - glm::angleAxis(e.z, Vectors::UNIT_Z)); + return (glm::angleAxis(e.x * RADIANS_PER_DEGREE, Vectors::UNIT_Y) * + glm::angleAxis(e.y * RADIANS_PER_DEGREE, Vectors::UNIT_X) * + glm::angleAxis(e.z * RADIANS_PER_DEGREE, Vectors::UNIT_Z)); } -#ifdef HAVE_NEURON - // // neuronDataReader SDK callback functions // @@ -355,21 +287,6 @@ void FrameDataReceivedCallback(void* context, SOCKET_REF sender, BvhDataHeaderEx // copy the data memcpy(&(neuronPlugin->_joints[0]), data, sizeof(NeuronPlugin::NeuronJoint) * NUM_JOINTS); - - } else { - qCWarning(inputplugins) << "NeuronPlugin: unsuported binary format, please enable displacements"; - - // enter mutex - std::lock_guard guard(neuronPlugin->_jointsMutex); - - if (neuronPlugin->_joints.size() != NeuronJointIndex::Size) { - neuronPlugin->_joints.resize(NeuronJointIndex::Size, { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); - } - - for (int i = 0; i < NeuronJointIndex::Size; i++) { - neuronPlugin->_joints[i].euler = glm::vec3(); - neuronPlugin->_joints[i].pos = neuronJointTranslations[i]; - } } } else { static bool ONCE = false; @@ -435,26 +352,19 @@ static void SocketStatusChangedCallback(void* context, SOCKET_REF sender, Socket qCDebug(inputplugins) << "NeuronPlugin: socket status = " << message; } -#endif // #ifdef HAVE_NEURON - // // NeuronPlugin // bool NeuronPlugin::isSupported() const { -#ifdef HAVE_NEURON // Because it's a client/server network architecture, we can't tell // if the neuron is actually connected until we connect to the server. return true; -#else - return false; -#endif } bool NeuronPlugin::activate() { InputPlugin::activate(); -#ifdef HAVE_NEURON // register with userInputMapper auto userInputMapper = DependencyManager::get(); userInputMapper->registerDevice(_inputDevice); @@ -480,13 +390,9 @@ bool NeuronPlugin::activate() { BRRegisterAutoSyncParmeter(_socketRef, Cmd_CombinationMode); return true; } -#else - return false; -#endif } void NeuronPlugin::deactivate() { -#ifdef HAVE_NEURON // unregister from userInputMapper if (_inputDevice->_deviceID != controller::Input::INVALID_DEVICE) { auto userInputMapper = DependencyManager::get(); @@ -499,10 +405,9 @@ void NeuronPlugin::deactivate() { } InputPlugin::deactivate(); -#endif } -void NeuronPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void NeuronPlugin::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { std::vector joints; { // lock and copy @@ -548,16 +453,23 @@ QString NeuronPlugin::InputDevice::getDefaultMappingConfig() const { void NeuronPlugin::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const std::vector& joints, const std::vector& prevJoints) { for (size_t i = 0; i < joints.size(); i++) { + int poseIndex = neuronJointIndexToPoseIndex((NeuronJointIndex)i); glm::vec3 linearVel, angularVel; - glm::vec3 pos = joints[i].pos; - glm::quat rot = eulerToQuat(joints[i].euler); + const glm::vec3& pos = joints[i].pos; + const glm::vec3& rotEuler = joints[i].euler; + + if (Vectors::ZERO == pos && Vectors::ZERO == rotEuler) { + _poseStateMap[poseIndex] = controller::Pose(); + continue; + } + + glm::quat rot = eulerToQuat(rotEuler); if (i < prevJoints.size()) { linearVel = (pos - (prevJoints[i].pos * METERS_PER_CENTIMETER)) / deltaTime; // m/s // quat log imaginary part points along the axis of rotation, with length of one half the angle of rotation. glm::quat d = glm::log(rot * glm::inverse(eulerToQuat(prevJoints[i].euler))); angularVel = glm::vec3(d.x, d.y, d.z) / (0.5f * deltaTime); // radians/s } - int poseIndex = neuronJointIndexToPoseIndex((NeuronJointIndex)i); _poseStateMap[poseIndex] = controller::Pose(pos, rot, linearVel, angularVel); } diff --git a/plugins/hifiNeuron/src/NeuronPlugin.h b/plugins/hifiNeuron/src/NeuronPlugin.h index 99859dcacb..9ddd79c013 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.h +++ b/plugins/hifiNeuron/src/NeuronPlugin.h @@ -27,7 +27,6 @@ public: // Plugin functions virtual bool isSupported() const override; - virtual bool isJointController() const override { return true; } virtual const QString& getName() const override { return NAME; } const QString& getID() const override { return NEURON_ID_STRING; } @@ -35,7 +34,7 @@ public: virtual void deactivate() override; virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void saveSettings() const override; virtual void loadSettings() override; @@ -56,7 +55,7 @@ protected: // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override {}; + virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override {}; virtual void focusOutEvent() override {}; void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, const std::vector& joints, const std::vector& prevJoints); diff --git a/plugins/hifiSdl2/src/Joystick.cpp b/plugins/hifiSdl2/src/Joystick.cpp index 9d195fd606..a109656489 100644 --- a/plugins/hifiSdl2/src/Joystick.cpp +++ b/plugins/hifiSdl2/src/Joystick.cpp @@ -15,7 +15,6 @@ const float CONTROLLER_THRESHOLD = 0.3f; -#ifdef HAVE_SDL2 const float MAX_AXIS = 32768.0f; Joystick::Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController) : @@ -27,19 +26,15 @@ Joystick::Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameControl } -#endif - Joystick::~Joystick() { closeJoystick(); } void Joystick::closeJoystick() { -#ifdef HAVE_SDL2 SDL_GameControllerClose(_sdlGameController); -#endif } -void Joystick::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void Joystick::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { for (auto axisState : _axisStateMap) { if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { _axisStateMap[axisState.first] = 0.0f; @@ -52,8 +47,6 @@ void Joystick::focusOutEvent() { _buttonPressedMap.clear(); }; -#ifdef HAVE_SDL2 - void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; _axisStateMap[makeInput((controller::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS; @@ -69,8 +62,6 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { } } -#endif - controller::Input::NamedVector Joystick::getAvailableInputs() const { using namespace controller; static const Input::NamedVector availableInputs{ diff --git a/plugins/hifiSdl2/src/Joystick.h b/plugins/hifiSdl2/src/Joystick.h index 08bf27b960..e2eaeaef8b 100644 --- a/plugins/hifiSdl2/src/Joystick.h +++ b/plugins/hifiSdl2/src/Joystick.h @@ -15,10 +15,8 @@ #include #include -#ifdef HAVE_SDL2 #include #undef main -#endif #include #include @@ -26,10 +24,7 @@ class Joystick : public QObject, public controller::InputDevice { Q_OBJECT Q_PROPERTY(QString name READ getName) - -#ifdef HAVE_SDL2 Q_PROPERTY(int instanceId READ getInstanceId) -#endif public: using Pointer = std::shared_ptr; @@ -39,33 +34,25 @@ public: // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void focusOutEvent() override; Joystick() : InputDevice("GamePad") {} ~Joystick(); -#ifdef HAVE_SDL2 Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController); -#endif void closeJoystick(); -#ifdef HAVE_SDL2 void handleAxisEvent(const SDL_ControllerAxisEvent& event); void handleButtonEvent(const SDL_ControllerButtonEvent& event); -#endif -#ifdef HAVE_SDL2 int getInstanceId() const { return _instanceId; } -#endif private: -#ifdef HAVE_SDL2 SDL_GameController* _sdlGameController; SDL_Joystick* _sdlJoystick; SDL_JoystickID _instanceId; -#endif }; #endif // hifi_Joystick_h diff --git a/plugins/hifiSdl2/src/SDL2Manager.cpp b/plugins/hifiSdl2/src/SDL2Manager.cpp index 7091b20d21..0bdb68f830 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.cpp +++ b/plugins/hifiSdl2/src/SDL2Manager.cpp @@ -16,7 +16,6 @@ #include "SDL2Manager.h" -#ifdef HAVE_SDL2 static_assert( (int)controller::A == (int)SDL_CONTROLLER_BUTTON_A && (int)controller::B == (int)SDL_CONTROLLER_BUTTON_B && @@ -40,28 +39,16 @@ static_assert( (int)controller::LT == (int)SDL_CONTROLLER_AXIS_TRIGGERLEFT && (int)controller::RT == (int)SDL_CONTROLLER_AXIS_TRIGGERRIGHT, "SDL2 equvalence: Enums and values from StandardControls.h are assumed to match enums from SDL_gamecontroller.h"); -#endif const QString SDL2Manager::NAME = "SDL2"; -#ifdef HAVE_SDL2 SDL_JoystickID SDL2Manager::getInstanceId(SDL_GameController* controller) { SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller); return SDL_JoystickInstanceID(joystick); } -#endif - -SDL2Manager::SDL2Manager() : -#ifdef HAVE_SDL2 -_openJoysticks(), -#endif -_isInitialized(false) -{ -} void SDL2Manager::init() { -#ifdef HAVE_SDL2 bool initSuccess = (SDL_Init(SDL_INIT_GAMECONTROLLER) == 0); if (initSuccess) { @@ -88,66 +75,50 @@ void SDL2Manager::init() { else { qDebug() << "Error initializing SDL2 Manager"; } -#endif } void SDL2Manager::deinit() { -#ifdef HAVE_SDL2 _openJoysticks.clear(); SDL_Quit(); -#endif } bool SDL2Manager::activate() { InputPlugin::activate(); -#ifdef HAVE_SDL2 auto userInputMapper = DependencyManager::get(); for (auto joystick : _openJoysticks) { userInputMapper->registerDevice(joystick); emit joystickAdded(joystick.get()); } return true; -#else - return false; -#endif } void SDL2Manager::deactivate() { -#ifdef HAVE_SDL2 auto userInputMapper = DependencyManager::get(); for (auto joystick : _openJoysticks) { userInputMapper->removeDevice(joystick->getDeviceID()); emit joystickRemoved(joystick.get()); } -#endif InputPlugin::deactivate(); } bool SDL2Manager::isSupported() const { -#ifdef HAVE_SDL2 return true; -#else - return false; -#endif } void SDL2Manager::pluginFocusOutEvent() { -#ifdef HAVE_SDL2 for (auto joystick : _openJoysticks) { joystick->focusOutEvent(); } -#endif } -void SDL2Manager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { -#ifdef HAVE_SDL2 +void SDL2Manager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { if (_isInitialized) { auto userInputMapper = DependencyManager::get(); for (auto joystick : _openJoysticks) { - joystick->update(deltaTime, inputCalibrationData, jointsCaptured); + joystick->update(deltaTime, inputCalibrationData); } PerformanceTimer perfTimer("SDL2Manager::update"); @@ -197,5 +168,4 @@ void SDL2Manager::pluginUpdate(float deltaTime, const controller::InputCalibrati } } } -#endif } diff --git a/plugins/hifiSdl2/src/SDL2Manager.h b/plugins/hifiSdl2/src/SDL2Manager.h index f69e23ee98..a597a87aee 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.h +++ b/plugins/hifiSdl2/src/SDL2Manager.h @@ -12,9 +12,7 @@ #ifndef hifi__SDL2Manager_h #define hifi__SDL2Manager_h -#ifdef HAVE_SDL2 #include -#endif #include #include @@ -24,30 +22,26 @@ class SDL2Manager : public InputPlugin { Q_OBJECT public: - SDL2Manager(); - // Plugin functions - virtual bool isSupported() const override; - virtual bool isJointController() const override { return false; } - virtual const QString& getName() const override { return NAME; } + bool isSupported() const override; + const QString& getName() const override { return NAME; } - virtual void init() override; - virtual void deinit() override; + void init() override; + void deinit() override; /// Called when a plugin is being activated for use. May be called multiple times. - virtual bool activate() override; + bool activate() override; /// Called when a plugin is no longer being used. May be called multiple times. - virtual void deactivate() override; + void deactivate() override; - virtual void pluginFocusOutEvent() override; - virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + void pluginFocusOutEvent() override; + void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; signals: void joystickAdded(Joystick* joystick); void joystickRemoved(Joystick* joystick); private: -#ifdef HAVE_SDL2 SDL_JoystickID getInstanceId(SDL_GameController* controller); int axisInvalid() const { return SDL_CONTROLLER_AXIS_INVALID; } @@ -81,8 +75,7 @@ private: int buttonRelease() const { return SDL_RELEASED; } QMap _openJoysticks; -#endif - bool _isInitialized; + bool _isInitialized { false } ; static const QString NAME; }; diff --git a/plugins/hifiSixense/src/SixenseManager.cpp b/plugins/hifiSixense/src/SixenseManager.cpp index fdb7bb17fe..9ea79a8b96 100644 --- a/plugins/hifiSixense/src/SixenseManager.cpp +++ b/plugins/hifiSixense/src/SixenseManager.cpp @@ -134,12 +134,12 @@ void SixenseManager::setSixenseFilter(bool filter) { #endif } -void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { BAIL_IF_NOT_LOADED auto userInputMapper = DependencyManager::get(); userInputMapper->withLock([&, this]() { - _inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured); + _inputDevice->update(deltaTime, inputCalibrationData); }); if (_inputDevice->_requestReset) { @@ -148,7 +148,7 @@ void SixenseManager::pluginUpdate(float deltaTime, const controller::InputCalibr } } -void SixenseManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void SixenseManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { BAIL_IF_NOT_LOADED #ifdef HAVE_SIXENSE _buttonPressedMap.clear(); @@ -208,14 +208,10 @@ void SixenseManager::InputDevice::update(float deltaTime, const controller::Inpu _axisStateMap[left ? LY : RY] = data->joystick_y; _axisStateMap[left ? LT : RT] = data->trigger; - if (!jointsCaptured) { - // Rotation of Palm - glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]); - handlePoseEvent(deltaTime, inputCalibrationData, position, rotation, left); - rawPoses[i] = controller::Pose(position, rotation, Vectors::ZERO, Vectors::ZERO); - } else { - _poseStateMap.clear(); - } + // Rotation of Palm + glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]); + handlePoseEvent(deltaTime, inputCalibrationData, position, rotation, left); + rawPoses[i] = controller::Pose(position, rotation, Vectors::ZERO, Vectors::ZERO); } else { auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; _poseStateMap[hand] = controller::Pose(); diff --git a/plugins/hifiSixense/src/SixenseManager.h b/plugins/hifiSixense/src/SixenseManager.h index a46614b17a..6aec9fd4ad 100644 --- a/plugins/hifiSixense/src/SixenseManager.h +++ b/plugins/hifiSixense/src/SixenseManager.h @@ -28,7 +28,6 @@ class SixenseManager : public InputPlugin { public: // Plugin functions virtual bool isSupported() const override; - virtual bool isJointController() const override { return true; } virtual const QString& getName() const override { return NAME; } virtual const QString& getID() const override { return HYDRA_ID_STRING; } @@ -36,7 +35,7 @@ public: virtual void deactivate() override; virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void saveSettings() const override; virtual void loadSettings() override; @@ -61,7 +60,7 @@ private: // Device functions virtual controller::Input::NamedVector getAvailableInputs() const override; virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void focusOutEvent() override; void handleButtonEvent(unsigned int buttons, bool left); diff --git a/plugins/hifiSpacemouse/CMakeLists.txt b/plugins/hifiSpacemouse/CMakeLists.txt new file mode 100644 index 0000000000..bcfb309a69 --- /dev/null +++ b/plugins/hifiSpacemouse/CMakeLists.txt @@ -0,0 +1,18 @@ +# +# Created by Bradley Austin Davis on 2016/05/11 +# Copyright 2013-2016 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 +# + +if(WIN32) + set(TARGET_NAME hifiSpacemouse) + find_package(3DCONNEXIONCLIENT) + if (3DCONNEXIONCLIENT_FOUND) + setup_hifi_plugin(Script Qml Widgets) + link_hifi_libraries(shared networking controllers ui plugins input-plugins) + target_include_directories(${TARGET_NAME} PUBLIC ${3DCONNEXIONCLIENT_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${3DCONNEXIONCLIENT_LIBRARIES}) + endif() +endif() diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp b/plugins/hifiSpacemouse/src/SpacemouseManager.cpp similarity index 95% rename from libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp rename to plugins/hifiSpacemouse/src/SpacemouseManager.cpp index d946990319..3d9b93ff44 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.cpp +++ b/plugins/hifiSpacemouse/src/SpacemouseManager.cpp @@ -11,19 +11,75 @@ #include "SpacemouseManager.h" +#ifdef Q_OS_WIN +#include +#endif + #include #include #include #include -#include "../../../interface/src/Menu.h" +const QString SpacemouseManager::NAME { "Spacemouse" }; const float MAX_AXIS = 75.0f; // max forward = 2x speed +#define LOGITECH_VENDOR_ID 0x46d -static std::shared_ptr instance = std::make_shared(); +#ifndef RIDEV_DEVNOTIFY +#define RIDEV_DEVNOTIFY 0x00002000 +#endif -SpacemouseDevice::SpacemouseDevice() : InputDevice("Spacemouse") +const int TRACE_RIDI_DEVICENAME = 0; +const int TRACE_RIDI_DEVICEINFO = 0; + +#ifdef _WIN64 +typedef unsigned __int64 QWORD; +#endif + +bool Is3dmouseAttached(); + +std::shared_ptr instance; + +bool SpacemouseManager::isSupported() const { + return Is3dmouseAttached(); +} + +bool SpacemouseManager::activate() { + fLast3dmouseInputTime = 0; + + InitializeRawInput(GetActiveWindow()); + + QAbstractEventDispatcher::instance()->installNativeEventFilter(this); + + if (!instance) { + instance = std::make_shared(); + } + + if (instance->getDeviceID() == controller::Input::INVALID_DEVICE) { + auto userInputMapper = DependencyManager::get(); + userInputMapper->registerDevice(instance); + UserActivityLogger::getInstance().connectedDevice("controller", NAME); + } + return true; +} + +void SpacemouseManager::deactivate() { + QAbstractEventDispatcher::instance()->removeNativeEventFilter(this); + int deviceid = instance->getDeviceID(); + auto userInputMapper = DependencyManager::get(); + userInputMapper->removeDevice(deviceid); +} + +void SpacemouseManager::pluginFocusOutEvent() { + instance->focusOutEvent(); +} + +void SpacemouseManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { + +} + +SpacemouseDevice::SpacemouseDevice() : InputDevice(SpacemouseManager::NAME) { } @@ -111,71 +167,78 @@ controller::Input::NamedPair SpacemouseDevice::makePair(SpacemouseDevice::Positi return controller::Input::NamedPair(makeInput(axis), name); } -void SpacemouseDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void SpacemouseDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { // the update is done in the SpacemouseManager class. // for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached // for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached } -void SpacemouseManager::ManagerFocusOutEvent() { - instance->focusOutEvent(); -} - -void SpacemouseManager::init() { -} - -#ifdef HAVE_3DCONNEXIONCLIENT - #ifdef Q_OS_WIN -#include - -void SpacemouseManager::toggleSpacemouse(bool shouldEnable) { - if (shouldEnable) { - init(); - } - if (!shouldEnable && instance->getDeviceID() != controller::Input::INVALID_DEVICE) { - destroy(); - } +bool SpacemouseManager::nativeEventFilter(const QByteArray& eventType, void* message, long* result) { + MSG* msg = static_cast< MSG * >(message); + return RawInputEventFilter(message, result); } -void SpacemouseManager::init() { - if (Menu::getInstance()->isOptionChecked(MenuOption::Connexion)) { - fLast3dmouseInputTime = 0; - InitializeRawInput(GetActiveWindow()); +//Get an initialized array of PRAWINPUTDEVICE for the 3D devices +//pNumDevices returns the number of devices to register. Currently this is always 1. +static PRAWINPUTDEVICE GetDevicesToRegister(unsigned int* pNumDevices) { + // Array of raw input devices to register + static RAWINPUTDEVICE sRawInputDevices[] = { + { 0x01, 0x08, 0x00, 0x00 } // Usage Page = 0x01 Generic Desktop Page, Usage Id= 0x08 Multi-axis Controller + }; - QAbstractEventDispatcher::instance()->installNativeEventFilter(this); + if (pNumDevices) { + *pNumDevices = sizeof(sRawInputDevices) / sizeof(sRawInputDevices[0]); + } - if (instance->getDeviceID() != controller::Input::INVALID_DEVICE) { - auto userInputMapper = DependencyManager::get(); - userInputMapper->registerDevice(instance); - UserActivityLogger::getInstance().connectedDevice("controller", "Spacemouse"); + return sRawInputDevices; +} + + +//Detect the 3D mouse +bool Is3dmouseAttached() { + unsigned int numDevicesOfInterest = 0; + PRAWINPUTDEVICE devicesToRegister = GetDevicesToRegister(&numDevicesOfInterest); + + unsigned int nDevices = 0; + + if (::GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) { + return false; + } + + if (nDevices == 0) { + return false; + } + + std::vector rawInputDeviceList(nDevices); + if (::GetRawInputDeviceList(&rawInputDeviceList[0], &nDevices, sizeof(RAWINPUTDEVICELIST)) == static_cast(-1)) { + return false; + } + + for (unsigned int i = 0; i < nDevices; ++i) { + RID_DEVICE_INFO rdi = { sizeof(rdi) }; + unsigned int cbSize = sizeof(rdi); + + if (GetRawInputDeviceInfo(rawInputDeviceList[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) > 0) { + //skip non HID and non logitec (3DConnexion) devices + if (rdi.dwType != RIM_TYPEHID || rdi.hid.dwVendorId != LOGITECH_VENDOR_ID) { + continue; + } + + //check if devices matches Multi-axis Controller + for (unsigned int j = 0; j < numDevicesOfInterest; ++j) { + if (devicesToRegister[j].usUsage == rdi.hid.usUsage + && devicesToRegister[j].usUsagePage == rdi.hid.usUsagePage) { + return true; + } + } } - } + return false; } -void SpacemouseManager::destroy() { - QAbstractEventDispatcher::instance()->removeNativeEventFilter(this); - int deviceid = instance->getDeviceID(); - auto userInputMapper = DependencyManager::get(); - userInputMapper->removeDevice(deviceid); -} - -#define LOGITECH_VENDOR_ID 0x46d - -#ifndef RIDEV_DEVNOTIFY -#define RIDEV_DEVNOTIFY 0x00002000 -#endif - -const int TRACE_RIDI_DEVICENAME = 0; -const int TRACE_RIDI_DEVICEINFO = 0; - -#ifdef _WIN64 -typedef unsigned __int64 QWORD; -#endif - // object angular velocity per mouse tick 0.008 milliradians per second per count static const double k3dmouseAngularVelocity = 8.0e-6; // radians per second per count @@ -290,20 +353,9 @@ bool SpacemouseManager::RawInputEventFilter(void* msg, long* result) { return false; } -// Access the mouse parameters structure -I3dMouseParam& SpacemouseManager::MouseParams() { - return f3dMouseParams; -} - -// Access the mouse parameters structure -const I3dMouseParam& SpacemouseManager::MouseParams() const { - return f3dMouseParams; -} - //Called with the processed motion data when a 3D mouse event is received void SpacemouseManager::Move3d(HANDLE device, std::vector& motionData) { Q_UNUSED(device); - instance->cc_position = { motionData[0] * 1000, motionData[1] * 1000, motionData[2] * 1000 }; instance->cc_rotation = { motionData[3] * 1500, motionData[4] * 1500, motionData[5] * 1500 }; instance->handleAxisEvent(); @@ -321,62 +373,6 @@ void SpacemouseManager::On3dmouseKeyUp(HANDLE device, int virtualKeyCode) { instance->setButton(0); } -//Get an initialized array of PRAWINPUTDEVICE for the 3D devices -//pNumDevices returns the number of devices to register. Currently this is always 1. -static PRAWINPUTDEVICE GetDevicesToRegister(unsigned int* pNumDevices) { - // Array of raw input devices to register - static RAWINPUTDEVICE sRawInputDevices[] = { - { 0x01, 0x08, 0x00, 0x00 } // Usage Page = 0x01 Generic Desktop Page, Usage Id= 0x08 Multi-axis Controller - }; - - if (pNumDevices) { - *pNumDevices = sizeof(sRawInputDevices) / sizeof(sRawInputDevices[0]); - } - - return sRawInputDevices; -} - -//Detect the 3D mouse -bool SpacemouseManager::Is3dmouseAttached() { - unsigned int numDevicesOfInterest = 0; - PRAWINPUTDEVICE devicesToRegister = GetDevicesToRegister(&numDevicesOfInterest); - - unsigned int nDevices = 0; - - if (::GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0) { - return false; - } - - if (nDevices == 0) { - return false; - } - - std::vector rawInputDeviceList(nDevices); - if (::GetRawInputDeviceList(&rawInputDeviceList[0], &nDevices, sizeof(RAWINPUTDEVICELIST)) == static_cast(-1)) { - return false; - } - - for (unsigned int i = 0; i < nDevices; ++i) { - RID_DEVICE_INFO rdi = { sizeof(rdi) }; - unsigned int cbSize = sizeof(rdi); - - if (GetRawInputDeviceInfo(rawInputDeviceList[i].hDevice, RIDI_DEVICEINFO, &rdi, &cbSize) > 0) { - //skip non HID and non logitec (3DConnexion) devices - if (rdi.dwType != RIM_TYPEHID || rdi.hid.dwVendorId != LOGITECH_VENDOR_ID) { - continue; - } - - //check if devices matches Multi-axis Controller - for (unsigned int j = 0; j < numDevicesOfInterest; ++j) { - if (devicesToRegister[j].usUsage == rdi.hid.usUsage - && devicesToRegister[j].usUsagePage == rdi.hid.usUsagePage) { - return true; - } - } - } - } - return false; -} // Initialize the window to recieve raw-input messages // This needs to be called initially so that Windows will send the messages from the 3D mouse to the window. @@ -942,5 +938,3 @@ void MessageHandler(unsigned int connection, unsigned int messageType, void *mes } #endif // __APPLE__ - -#endif diff --git a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h b/plugins/hifiSpacemouse/src/SpacemouseManager.h similarity index 77% rename from libraries/input-plugins/src/input-plugins/SpacemouseManager.h rename to plugins/hifiSpacemouse/src/SpacemouseManager.h index 82c4fa8fb6..a9933902e5 100644 --- a/libraries/input-plugins/src/input-plugins/SpacemouseManager.h +++ b/plugins/hifiSpacemouse/src/SpacemouseManager.h @@ -17,22 +17,8 @@ #include #include -#include "InputPlugin.h" +#include -#ifndef HAVE_3DCONNEXIONCLIENT -class SpacemouseManager : public QObject { - Q_OBJECT -public: - void ManagerFocusOutEvent(); - void init(); - void destroy() {}; - bool Is3dmouseAttached() { return false; }; - public slots: - void toggleSpacemouse(bool shouldEnable) {}; -}; -#endif - -#ifdef HAVE_3DCONNEXIONCLIENT // the windows connexion rawinput #ifdef Q_OS_WIN @@ -85,42 +71,26 @@ private: Speed fSpeed; }; -class SpacemouseManager : public QObject, public QAbstractNativeEventFilter { +class SpacemouseManager : public InputPlugin, public QAbstractNativeEventFilter { Q_OBJECT public: - SpacemouseManager() {}; + bool isSupported() const override; + const QString& getName() const override { return NAME; } + const QString& getID() const override { return NAME; } - void init(); - void destroy(); - bool Is3dmouseAttached(); + bool activate() override; + void deactivate() override; - SpacemouseManager* client; + void pluginFocusOutEvent() override; + void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; - void ManagerFocusOutEvent(); - - I3dMouseParam& MouseParams(); - const I3dMouseParam& MouseParams() const; - - virtual void Move3d(HANDLE device, std::vector& motionData); - virtual void On3dmouseKeyDown(HANDLE device, int virtualKeyCode); - virtual void On3dmouseKeyUp(HANDLE device, int virtualKeyCode); - - virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) Q_DECL_OVERRIDE - { - MSG* msg = static_cast< MSG * >(message); - return RawInputEventFilter(message, result); - } - - public slots: - void toggleSpacemouse(bool shouldEnable); - -signals: - void Move3d(std::vector& motionData); - void On3dmouseKeyDown(int virtualKeyCode); - void On3dmouseKeyUp(int virtualKeyCode); + bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; private: + void Move3d(HANDLE device, std::vector& motionData); + void On3dmouseKeyDown(HANDLE device, int virtualKeyCode); + void On3dmouseKeyUp(HANDLE device, int virtualKeyCode); bool InitializeRawInput(HWND hwndTarget); bool RawInputEventFilter(void* msg, long* result); @@ -156,6 +126,9 @@ private: // use to calculate distance traveled since last event DWORD fLast3dmouseInputTime; + + static const QString NAME; + friend class SpacemouseDevice; }; // the osx connexion api @@ -176,8 +149,6 @@ public: #endif // __APPLE__ -#endif - // connnects to the userinputmapper class SpacemouseDevice : public QObject, public controller::InputDevice { @@ -214,7 +185,7 @@ public: virtual controller::Input::NamedVector getAvailableInputs() const override; virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; virtual void focusOutEvent() override; glm::vec3 cc_position; diff --git a/plugins/hifiSpacemouse/src/SpacemouseProvider.cpp b/plugins/hifiSpacemouse/src/SpacemouseProvider.cpp new file mode 100644 index 0000000000..c623f77d73 --- /dev/null +++ b/plugins/hifiSpacemouse/src/SpacemouseProvider.cpp @@ -0,0 +1,45 @@ +// +// Created by Bradley Austin Davis on 2015/10/25 +// Copyright 2015 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 +// + +#include + +#include +#include +#include + +#include +#include + +#include "SpacemouseManager.h" + +class SpacemouseProvider : public QObject, public InputProvider +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID InputProvider_iid FILE "plugin.json") + Q_INTERFACES(InputProvider) + +public: + SpacemouseProvider(QObject* parent = nullptr) : QObject(parent) {} + virtual ~SpacemouseProvider() {} + + virtual InputPluginList getInputPlugins() override { + static std::once_flag once; + std::call_once(once, [&] { + InputPluginPointer plugin(new SpacemouseManager()); + if (plugin->isSupported()) { + _inputPlugins.push_back(plugin); + } + }); + return _inputPlugins; + } + +private: + InputPluginList _inputPlugins; +}; + +#include "SpacemouseProvider.moc" diff --git a/plugins/hifiSpacemouse/src/plugin.json b/plugins/hifiSpacemouse/src/plugin.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/plugins/hifiSpacemouse/src/plugin.json @@ -0,0 +1 @@ +{} diff --git a/plugins/oculus/src/OculusControllerManager.cpp b/plugins/oculus/src/OculusControllerManager.cpp index 50ef6b09a1..09ab6ec159 100644 --- a/plugins/oculus/src/OculusControllerManager.cpp +++ b/plugins/oculus/src/OculusControllerManager.cpp @@ -76,12 +76,12 @@ void OculusControllerManager::deactivate() { } } -void OculusControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void OculusControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { PerformanceTimer perfTimer("OculusControllerManager::TouchDevice::update"); if (_touch) { if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Touch, &_inputState))) { - _touch->update(deltaTime, inputCalibrationData, jointsCaptured); + _touch->update(deltaTime, inputCalibrationData); } else { qCWarning(oculus) << "Unable to read Oculus touch input state"; } @@ -89,7 +89,7 @@ void OculusControllerManager::pluginUpdate(float deltaTime, const controller::In if (_remote) { if (OVR_SUCCESS(ovr_GetInputState(_session, ovrControllerType_Remote, &_inputState))) { - _remote->update(deltaTime, inputCalibrationData, jointsCaptured); + _remote->update(deltaTime, inputCalibrationData); } else { qCWarning(oculus) << "Unable to read Oculus remote input state"; } @@ -158,7 +158,7 @@ QString OculusControllerManager::RemoteDevice::getDefaultMappingConfig() const { return MAPPING_JSON; } -void OculusControllerManager::RemoteDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void OculusControllerManager::RemoteDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _buttonPressedMap.clear(); const auto& inputState = _parent._inputState; for (const auto& pair : BUTTON_MAP) { @@ -172,21 +172,19 @@ void OculusControllerManager::RemoteDevice::focusOutEvent() { _buttonPressedMap.clear(); } -void OculusControllerManager::TouchDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void OculusControllerManager::TouchDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _poseStateMap.clear(); _buttonPressedMap.clear(); - if (!jointsCaptured) { - int numTrackedControllers = 0; - static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked & ovrStatus_PositionTracked; - auto tracking = ovr_GetTrackingState(_parent._session, 0, false); - ovr_for_each_hand([&](ovrHandType hand) { - ++numTrackedControllers; - if (REQUIRED_HAND_STATUS == (tracking.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) { - handlePose(deltaTime, inputCalibrationData, hand, tracking.HandPoses[hand]); - } - }); - } + int numTrackedControllers = 0; + static const auto REQUIRED_HAND_STATUS = ovrStatus_OrientationTracked & ovrStatus_PositionTracked; + auto tracking = ovr_GetTrackingState(_parent._session, 0, false); + ovr_for_each_hand([&](ovrHandType hand) { + ++numTrackedControllers; + if (REQUIRED_HAND_STATUS == (tracking.HandStatusFlags[hand] & REQUIRED_HAND_STATUS)) { + handlePose(deltaTime, inputCalibrationData, hand, tracking.HandPoses[hand]); + } + }); using namespace controller; // Axes const auto& inputState = _parent._inputState; diff --git a/plugins/oculus/src/OculusControllerManager.h b/plugins/oculus/src/OculusControllerManager.h index 60969097f8..980e1286f8 100644 --- a/plugins/oculus/src/OculusControllerManager.h +++ b/plugins/oculus/src/OculusControllerManager.h @@ -24,14 +24,13 @@ class OculusControllerManager : public InputPlugin { public: // Plugin functions bool isSupported() const override; - bool isJointController() const override { return true; } const QString& getName() const override { return NAME; } bool activate() override; void deactivate() override; void pluginFocusOutEvent() override; - void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; private: class OculusInputDevice : public controller::InputDevice { @@ -49,7 +48,7 @@ private: controller::Input::NamedVector getAvailableInputs() const override; QString getDefaultMappingConfig() const override; - void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void focusOutEvent() override; friend class OculusControllerManager; @@ -62,7 +61,7 @@ private: controller::Input::NamedVector getAvailableInputs() const override; QString getDefaultMappingConfig() const override; - void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void focusOutEvent() override; private: diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 38719fdca5..dba8fca208 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -9,7 +9,6 @@ #include -#include #include #include #include @@ -30,10 +29,6 @@ Q_DECLARE_LOGGING_CATEGORY(displayplugins) const QString OpenVrDisplayPlugin::NAME("OpenVR (Vive)"); const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; // this probably shouldn't be hardcoded here -static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR"); -static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); - - static vr::IVRCompositor* _compositor{ nullptr }; vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; @@ -43,7 +38,7 @@ static mat4 _sensorResetMat; static std::array VR_EYES { { vr::Eye_Left, vr::Eye_Right } }; bool OpenVrDisplayPlugin::isSupported() const { - return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent(); + return openVrSupported(); } bool OpenVrDisplayPlugin::internalActivate() { diff --git a/plugins/openvr/src/OpenVrHelpers.cpp b/plugins/openvr/src/OpenVrHelpers.cpp index 8ddf028dd2..8536ffd5d9 100644 --- a/plugins/openvr/src/OpenVrHelpers.cpp +++ b/plugins/openvr/src/OpenVrHelpers.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -44,6 +45,12 @@ bool isOculusPresent() { return result; } +bool openVrSupported() { + static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR"); + static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); + return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent(); +} + vr::IVRSystem* acquireOpenVrSystem() { bool hmdPresent = vr::VR_IsHmdPresent(); if (hmdPresent) { diff --git a/plugins/openvr/src/OpenVrHelpers.h b/plugins/openvr/src/OpenVrHelpers.h index 81896a2ce5..4b06ca0813 100644 --- a/plugins/openvr/src/OpenVrHelpers.h +++ b/plugins/openvr/src/OpenVrHelpers.h @@ -12,7 +12,8 @@ #include #include -bool isOculusPresent(); +bool openVrSupported(); + vr::IVRSystem* acquireOpenVrSystem(); void releaseOpenVrSystem(); diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index fab383a955..12567b10d1 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -50,11 +50,9 @@ static const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; static const QString RENDER_CONTROLLERS = "Render Hand Controllers"; const QString ViveControllerManager::NAME = "OpenVR"; -static const QString DEBUG_FLAG("HIFI_DEBUG_OPENVR"); -static bool enableDebugOpenVR = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG); bool ViveControllerManager::isSupported() const { - return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent(); + return openVrSupported(); } bool ViveControllerManager::activate() { @@ -214,12 +212,12 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& } -void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { auto userInputMapper = DependencyManager::get(); // because update mutates the internal state we need to lock userInputMapper->withLock([&, this]() { - _inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured); + _inputDevice->update(deltaTime, inputCalibrationData); }); if (_inputDevice->_trackedControllers == 0 && _registeredWithInputMapper) { @@ -235,7 +233,7 @@ void ViveControllerManager::pluginUpdate(float deltaTime, const controller::Inpu } } -void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) { +void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { _poseStateMap.clear(); _buttonPressedMap.clear(); @@ -244,10 +242,8 @@ void ViveControllerManager::InputDevice::update(float deltaTime, const controlle auto leftHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_LeftHand); auto rightHandDeviceIndex = _system->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); - if (!jointsCaptured) { - handleHandController(deltaTime, leftHandDeviceIndex, inputCalibrationData, true); - handleHandController(deltaTime, rightHandDeviceIndex, inputCalibrationData, false); - } + handleHandController(deltaTime, leftHandDeviceIndex, inputCalibrationData, true); + handleHandController(deltaTime, rightHandDeviceIndex, inputCalibrationData, false); int numTrackedControllers = 0; if (leftHandDeviceIndex != vr::k_unTrackedDeviceIndexInvalid) { diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index d55d4e726c..672ad59cfe 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -31,17 +31,15 @@ namespace vr { class ViveControllerManager : public InputPlugin { Q_OBJECT public: - // Plugin functions - virtual bool isSupported() const override; - virtual bool isJointController() const override { return true; } + bool isSupported() const override; const QString& getName() const override { return NAME; } - virtual bool activate() override; - virtual void deactivate() override; + bool activate() override; + void deactivate() override; - virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } - virtual void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; + void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } + void pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; void updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges); @@ -53,10 +51,10 @@ private: InputDevice(vr::IVRSystem*& system) : controller::InputDevice("Vive"), _system(system) {} private: // Device functions - virtual controller::Input::NamedVector getAvailableInputs() const override; - virtual QString getDefaultMappingConfig() const override; - virtual void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; - virtual void focusOutEvent() override; + controller::Input::NamedVector getAvailableInputs() const override; + QString getDefaultMappingConfig() const override; + void update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) override; + void focusOutEvent() override; void handleHandController(float deltaTime, uint32_t deviceIndex, const controller::InputCalibrationData& inputCalibrationData, bool isLeftHand); void handleButtonEvent(float deltaTime, uint32_t button, bool pressed, bool touched, bool isLeftHand); diff --git a/scripts/system/assets/images/lock.svg b/scripts/system/assets/images/lock.svg new file mode 100644 index 0000000000..bb9658de00 --- /dev/null +++ b/scripts/system/assets/images/lock.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/scripts/system/assets/images/unlock.svg b/scripts/system/assets/images/unlock.svg new file mode 100644 index 0000000000..789a8b0ed5 --- /dev/null +++ b/scripts/system/assets/images/unlock.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/scripts/system/attachedEntitiesManager.js b/scripts/system/attachedEntitiesManager.js index 9ddb040297..79c5973a6e 100644 --- a/scripts/system/attachedEntitiesManager.js +++ b/scripts/system/attachedEntitiesManager.js @@ -22,42 +22,28 @@ var MINIMUM_DROP_DISTANCE_FROM_JOINT = 0.8; var ATTACHED_ENTITY_SEARCH_DISTANCE = 10.0; var ATTACHED_ENTITIES_SETTINGS_KEY = "ATTACHED_ENTITIES"; var DRESSING_ROOM_DISTANCE = 2.0; -var SHOW_TOOL_BAR = false; +var SHOW_TOOL_BAR = true; // tool bar if (SHOW_TOOL_BAR) { - var BUTTON_SIZE = 32; - var PADDING = 3; + var BUTTON_SIZE = 64; + var PADDING = 6; Script.include(["libraries/toolBars.js"]); - var toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.attachedEntities.toolbar", function(screenSize) { - return { - x: (BUTTON_SIZE + PADDING), - y: (screenSize.y / 2 - BUTTON_SIZE * 2 + PADDING) - }; - }); - var saveButton = toolBar.addOverlay("image", { + + var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.attachedEntities.toolbar"); + var lockButton = toolBar.addTool({ width: BUTTON_SIZE, height: BUTTON_SIZE, - imageURL: ".../save.png", + imageURL: Script.resolvePath("assets/images/lock.svg"), color: { red: 255, green: 255, blue: 255 }, - alpha: 1 - }); - var loadButton = toolBar.addOverlay("image", { - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: ".../load.png", - color: { - red: 255, - green: 255, - blue: 255 - }, - alpha: 1 - }); + alpha: 1, + visible: true + }, false); } @@ -67,10 +53,8 @@ function mousePressEvent(event) { y: event.y }); - if (clickedOverlay == saveButton) { - manager.saveAttachedEntities(); - } else if (clickedOverlay == loadButton) { - manager.loadAttachedEntities(); + if (lockButton === toolBar.clicked(clickedOverlay)) { + manager.toggleLocked(); } } @@ -92,6 +76,8 @@ Script.scriptEnding.connect(scriptEnding); function AttachedEntitiesManager() { + var clothingLocked = true; + this.subscribeToMessages = function() { Messages.subscribe('Hifi-Object-Manipulation'); Messages.messageReceived.connect(this.handleWearableMessages); @@ -128,26 +114,14 @@ function AttachedEntitiesManager() { } } - this.avatarIsInDressingRoom = function() { - // return true if MyAvatar is near the dressing room - var possibleDressingRoom = Entities.findEntities(MyAvatar.position, DRESSING_ROOM_DISTANCE); - for (i = 0; i < possibleDressingRoom.length; i++) { - var entityID = possibleDressingRoom[i]; - var props = Entities.getEntityProperties(entityID); - if (props.name == 'Hifi-Dressing-Room-Base') { - return true; - } - } - return false; - } - this.handleEntityRelease = function(grabbedEntity, releasedFromJoint) { // if this is still equipped, just rewrite the position information. var grabData = getEntityCustomData('grabKey', grabbedEntity, {}); - if ("refCount" in grabData && grabData.refCount > 0) { - manager.updateRelativeOffsets(grabbedEntity); - return; - } + // if ("refCount" in grabData && grabData.refCount > 0) { + // // for adjusting things in your other hand + // manager.updateRelativeOffsets(grabbedEntity); + // return; + // } var allowedJoints = getEntityCustomData('wearable', grabbedEntity, DEFAULT_WEARABLE_DATA).joints; @@ -179,21 +153,27 @@ function AttachedEntitiesManager() { } if (bestJointIndex != -1) { - var wearProps = { - parentID: MyAvatar.sessionUUID, - parentJointIndex: bestJointIndex - }; + var wearProps = Entities.getEntityProperties(grabbedEntity); + wearProps.parentID = MyAvatar.sessionUUID; + wearProps.parentJointIndex = bestJointIndex; + var updatePresets = false; if (bestJointOffset && bestJointOffset.constructor === Array) { - if (this.avatarIsInDressingRoom() || bestJointOffset.length < 2) { - this.updateRelativeOffsets(grabbedEntity); + if (!clothingLocked || bestJointOffset.length < 2) { + // we're unlocked or this thing didn't have a preset position, so update it + updatePresets = true; } else { - // don't snap the entity to the preferred position if the avatar is in the dressing room. + // don't snap the entity to the preferred position if unlocked wearProps.localPosition = bestJointOffset[0]; wearProps.localRotation = bestJointOffset[1]; } } - Entities.editEntity(grabbedEntity, wearProps); + + Entities.deleteEntity(grabbedEntity); + grabbedEntity = Entities.addEntity(wearProps, true); + if (updatePresets) { + this.updateRelativeOffsets(grabbedEntity); + } } else if (props.parentID != NULL_UUID) { // drop the entity and set it to have no parent (not on the avatar), unless it's being equipped in a hand. if (props.parentID === MyAvatar.sessionUUID && @@ -201,7 +181,26 @@ function AttachedEntitiesManager() { props.parentJointIndex == MyAvatar.getJointIndex("LeftHand"))) { // this is equipped on a hand -- don't clear the parent. } else { - Entities.editEntity(grabbedEntity, { parentID: NULL_UUID }); + var wearProps = Entities.getEntityProperties(grabbedEntity); + wearProps.parentID = NULL_UUID; + wearProps.parentJointIndex = -1; + + delete wearProps.id; + delete wearProps.created; + delete wearProps.age; + delete wearProps.ageAsText; + delete wearProps.naturalDimensions; + delete wearProps.naturalPosition; + delete wearProps.actionData; + delete wearProps.sittingPoints; + delete wearProps.boundingBox; + delete wearProps.clientOnly; + delete wearProps.owningAvatarID; + delete wearProps.localPosition; + delete wearProps.localRotation; + + Entities.deleteEntity(grabbedEntity); + Entities.addEntity(wearProps); } } } @@ -221,21 +220,32 @@ function AttachedEntitiesManager() { return false; } - this.saveAttachedEntities = function() { - print("--- saving attached entities ---"); - saveData = []; - var nearbyEntities = Entities.findEntities(MyAvatar.position, ATTACHED_ENTITY_SEARCH_DISTANCE); - for (i = 0; i < nearbyEntities.length; i++) { - var entityID = nearbyEntities[i]; - if (this.updateRelativeOffsets(entityID)) { - var props = Entities.getEntityProperties(entityID); // refresh, because updateRelativeOffsets changed them - this.scrubProperties(props); - saveData.push(props); - } + this.toggleLocked = function() { + print("toggleLocked"); + if (clothingLocked) { + clothingLocked = false; + toolBar.setImageURL(Script.resolvePath("assets/images/unlock.svg"), lockButton); + } else { + clothingLocked = true; + toolBar.setImageURL(Script.resolvePath("assets/images/lock.svg"), lockButton); } - Settings.setValue(ATTACHED_ENTITIES_SETTINGS_KEY, JSON.stringify(saveData)); } + // this.saveAttachedEntities = function() { + // print("--- saving attached entities ---"); + // saveData = []; + // var nearbyEntities = Entities.findEntities(MyAvatar.position, ATTACHED_ENTITY_SEARCH_DISTANCE); + // for (i = 0; i < nearbyEntities.length; i++) { + // var entityID = nearbyEntities[i]; + // if (this.updateRelativeOffsets(entityID)) { + // var props = Entities.getEntityProperties(entityID); // refresh, because updateRelativeOffsets changed them + // this.scrubProperties(props); + // saveData.push(props); + // } + // } + // Settings.setValue(ATTACHED_ENTITIES_SETTINGS_KEY, JSON.stringify(saveData)); + // } + this.scrubProperties = function(props) { var toScrub = ["queryAACube", "position", "rotation", "created", "ageAsText", "naturalDimensions", @@ -258,37 +268,37 @@ function AttachedEntitiesManager() { } } - this.loadAttachedEntities = function(grabbedEntity) { - print("--- loading attached entities ---"); - jsonAttachmentData = Settings.getValue(ATTACHED_ENTITIES_SETTINGS_KEY); - var loadData = []; - try { - loadData = JSON.parse(jsonAttachmentData); - } catch (e) { - print('error parsing saved attachment data'); - return; - } + // this.loadAttachedEntities = function(grabbedEntity) { + // print("--- loading attached entities ---"); + // jsonAttachmentData = Settings.getValue(ATTACHED_ENTITIES_SETTINGS_KEY); + // var loadData = []; + // try { + // loadData = JSON.parse(jsonAttachmentData); + // } catch (e) { + // print('error parsing saved attachment data'); + // return; + // } - for (i = 0; i < loadData.length; i ++) { - var savedProps = loadData[ i ]; - var currentProps = Entities.getEntityProperties(savedProps.id); - if (currentProps.id == savedProps.id && - // TODO -- also check that parentJointIndex matches? - currentProps.parentID == MyAvatar.sessionUUID) { - // entity is already in-world. TODO -- patch it up? - continue; - } - this.scrubProperties(savedProps); - delete savedProps["id"]; - savedProps.parentID = MyAvatar.sessionUUID; // this will change between sessions - var loadedEntityID = Entities.addEntity(savedProps); + // for (i = 0; i < loadData.length; i ++) { + // var savedProps = loadData[ i ]; + // var currentProps = Entities.getEntityProperties(savedProps.id); + // if (currentProps.id == savedProps.id && + // // TODO -- also check that parentJointIndex matches? + // currentProps.parentID == MyAvatar.sessionUUID) { + // // entity is already in-world. TODO -- patch it up? + // continue; + // } + // this.scrubProperties(savedProps); + // delete savedProps["id"]; + // savedProps.parentID = MyAvatar.sessionUUID; // this will change between sessions + // var loadedEntityID = Entities.addEntity(savedProps, true); - Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ - action: 'loaded', - grabbedEntity: loadedEntityID - })); - } - } + // Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + // action: 'loaded', + // grabbedEntity: loadedEntityID + // })); + // } + // } } var manager = new AttachedEntitiesManager(); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index ed4ac219c0..06549a38b5 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1112,8 +1112,8 @@ function MyController(hand) { Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand); // transform it into world frame - var controllerPosition = Vec3.sum(MyAvatar.position, - Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); + var controllerPositionVSAvatar = Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation); + var controllerPosition = Vec3.sum(MyAvatar.position, controllerPositionVSAvatar); var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); @@ -1161,7 +1161,7 @@ function MyController(hand) { this.turnOffVisualizations(); - this.previousControllerPosition = controllerPosition; + this.previousControllerPositionVSAvatar = controllerPositionVSAvatar; this.previousControllerRotation = controllerRotation; }; @@ -1179,8 +1179,8 @@ function MyController(hand) { Controller.Standard.RightHand : Controller.Standard.LeftHand); // transform it into world frame - var controllerPosition = Vec3.sum(MyAvatar.position, - Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation)); + var controllerPositionVSAvatar = Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation); + var controllerPosition = Vec3.sum(MyAvatar.position, controllerPositionVSAvatar); var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); @@ -1197,7 +1197,8 @@ function MyController(hand) { } // scale delta controller hand movement by radius. - var handMoved = Vec3.multiply(Vec3.subtract(controllerPosition, this.previousControllerPosition), radius); + var handMoved = Vec3.multiply(Vec3.subtract(controllerPositionVSAvatar, this.previousControllerPositionVSAvatar), + radius); // double delta controller rotation var handChange = Quat.multiply(Quat.slerp(this.previousControllerRotation, @@ -1218,7 +1219,7 @@ function MyController(hand) { var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData); // Update radialVelocity - var lastVelocity = Vec3.subtract(controllerPosition, this.previousControllerPosition); + var lastVelocity = Vec3.subtract(controllerPositionVSAvatar, this.previousControllerPositionVSAvatar); lastVelocity = Vec3.multiply(lastVelocity, 1.0 / deltaTime); var newRadialVelocity = Vec3.dot(lastVelocity, Vec3.normalize(Vec3.subtract(grabbedProperties.position, controllerPosition))); @@ -1266,7 +1267,9 @@ function MyController(hand) { var clampedVector; var targetPosition; if (constraintData.axisStart !== false) { - clampedVector = this.projectVectorAlongAxis(this.currentObjectPosition, constraintData.axisStart, constraintData.axisEnd); + clampedVector = this.projectVectorAlongAxis(this.currentObjectPosition, + constraintData.axisStart, + constraintData.axisEnd); targetPosition = clampedVector; } else { targetPosition = { @@ -1309,7 +1312,7 @@ function MyController(hand) { print("continueDistanceHolding -- updateAction failed"); } - this.previousControllerPosition = controllerPosition; + this.previousControllerPositionVSAvatar = controllerPositionVSAvatar; this.previousControllerRotation = controllerRotation; }; diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 78b7c4eb84..f4e4492a88 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -178,7 +178,10 @@ var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.01; function updateSeeking() { if (!Reticle.visible || isShakingMouse()) { - isSeeking = true; + if (!isSeeking) { + print('Start seeking mouse.'); + isSeeking = true; + } } // e.g., if we're about to turn it on with first movement. if (!isSeeking) { return; @@ -203,6 +206,7 @@ function updateSeeking() { } var okX = !updateDimension('x'), okY = !updateDimension('y'); // Evaluate both. Don't short-circuit. if (okX && okY) { + print('Finished seeking mouse'); isSeeking = false; } else { Reticle.setPosition(copy); // Not setReticlePosition @@ -444,7 +448,7 @@ function update() { updateVisualization(controllerPosition, controllerDirection, hudPoint3d, hudPoint2d); } -var UPDATE_INTERVAL = 20; // milliseconds. Script.update is too frequent. +var UPDATE_INTERVAL = 50; // milliseconds. Script.update is too frequent. var updater = Script.setInterval(update, UPDATE_INTERVAL); Script.scriptEnding.connect(function () { Script.clearInterval(updater); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index e84cdf7971..afbc679ec4 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -352,7 +352,9 @@ var toolBar = (function() { gridTool.setVisible(true); grid.setEnabled(true); propertiesTool.setVisible(true); - Window.setFocus(); + // Not sure what the following was meant to accomplish, but it currently causes + // everybody else to think that Interface has lost focus overall. fogbugzid:558 + // Window.setFocus(); } that.showTools(isActive); } diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index ae5684d6c8..efe7e6cc65 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -484,6 +484,9 @@ var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green"); var elZoneSkyboxColorBlue = document.getElementById("property-zone-skybox-color-blue"); var elZoneSkyboxURL = document.getElementById("property-zone-skybox-url"); + + var elZoneFlyingAllowed = document.getElementById("property-zone-flying-allowed"); + var elZoneGhostingAllowed = document.getElementById("property-zone-ghosting-allowed"); var elPolyVoxSections = document.querySelectorAll(".poly-vox-section"); allSections.push(elPolyVoxSections); @@ -626,18 +629,19 @@ var parsedUserData = {} try { parsedUserData = JSON.parse(properties.userData); + + if ("grabbableKey" in parsedUserData) { + if ("grabbable" in parsedUserData["grabbableKey"]) { + elGrabbable.checked = parsedUserData["grabbableKey"].grabbable; + } + if ("wantsTrigger" in parsedUserData["grabbableKey"]) { + elWantsTrigger.checked = parsedUserData["grabbableKey"].wantsTrigger; + } + if ("ignoreIK" in parsedUserData["grabbableKey"]) { + elIgnoreIK.checked = parsedUserData["grabbableKey"].ignoreIK; + } + } } catch(e) {} - if ("grabbableKey" in parsedUserData) { - if ("grabbable" in parsedUserData["grabbableKey"]) { - elGrabbable.checked = parsedUserData["grabbableKey"].grabbable; - } - if ("wantsTrigger" in parsedUserData["grabbableKey"]) { - elWantsTrigger.checked = parsedUserData["grabbableKey"].wantsTrigger; - } - if ("ignoreIK" in parsedUserData["grabbableKey"]) { - elIgnoreIK.checked = parsedUserData["grabbableKey"].ignoreIK; - } - } elCollisionSoundURL.value = properties.collisionSoundURL; elLifetime.value = properties.lifetime; @@ -770,6 +774,9 @@ elZoneSkyboxColorGreen.value = properties.skybox.color.green; elZoneSkyboxColorBlue.value = properties.skybox.color.blue; elZoneSkyboxURL.value = properties.skybox.url; + + elZoneFlyingAllowed.checked = properties.flyingAllowed; + elZoneGhostingAllowed.checked = properties.ghostingAllowed; showElements(document.getElementsByClassName('skybox-section'), elZoneBackgroundMode.value == 'skybox'); } else if (properties.type == "PolyVox") { @@ -1076,7 +1083,10 @@ })); elZoneSkyboxURL.addEventListener('change', createEmitGroupTextPropertyUpdateFunction('skybox','url')); - + + elZoneFlyingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('flyingAllowed')); + elZoneGhostingAllowed.addEventListener('change', createEmitCheckedPropertyUpdateFunction('ghostingAllowed')); + var voxelVolumeSizeChangeFunction = createEmitVec3PropertyUpdateFunction( 'voxelVolumeSize', elVoxelVolumeSizeX, elVoxelVolumeSizeY, elVoxelVolumeSizeZ); elVoxelVolumeSizeX.addEventListener('change', voxelVolumeSizeChangeFunction); @@ -1791,6 +1801,15 @@ +
+ + +
+
+ + +
+
M diff --git a/scripts/system/libraries/toolBars.js b/scripts/system/libraries/toolBars.js index d97575d349..9efe533457 100644 --- a/scripts/system/libraries/toolBars.js +++ b/scripts/system/libraries/toolBars.js @@ -56,6 +56,10 @@ Overlay2D = function(properties, overlay) { // overlay is an optional variable properties.alpha = alpha; Overlays.editOverlay(overlay, { alpha: alpha }); } + this.setImageURL = function(imageURL) { + properties.imageURL = imageURL; + Overlays.editOverlay(overlay, { imageURL: imageURL }); + } this.show = function(doShow) { properties.visible = doShow; Overlays.editOverlay(overlay, { visible: doShow }); @@ -254,7 +258,7 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit } this.save(); } - + this.setAlpha = function(alpha, tool) { if(typeof(tool) === 'undefined') { for(var tool in this.tools) { @@ -268,7 +272,11 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit this.tools[tool].setAlpha(alpha); } } - + + this.setImageURL = function(imageURL, tool) { + this.tools[tool].setImageURL(imageURL); + } + this.setBack = function(color, alpha) { if (color == null) { Overlays.editOverlay(this.back, { visible: false }); @@ -478,4 +486,4 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit } ToolBar.SPACING = 6; ToolBar.VERTICAL = 0; -ToolBar.HORIZONTAL = 1; \ No newline at end of file +ToolBar.HORIZONTAL = 1; diff --git a/scripts/system/users.js b/scripts/system/users.js index d935dd23ca..c010b7ea24 100644 --- a/scripts/system/users.js +++ b/scripts/system/users.js @@ -716,7 +716,7 @@ var usersWindow = (function () { if (clickedOverlay === windowPane) { - overlayX = event.x - WINDOW_MARGIN; + overlayX = event.x - windowPosition.x - WINDOW_MARGIN; overlayY = event.y - windowPosition.y + windowHeight - WINDOW_MARGIN - windowLineHeight; numLinesBefore = Math.round(overlayY / windowLineHeight); diff --git a/tests/controllers/src/main.cpp b/tests/controllers/src/main.cpp index e978dd9a38..36ed566ea7 100644 --- a/tests/controllers/src/main.cpp +++ b/tests/controllers/src/main.cpp @@ -89,7 +89,7 @@ public: virtual GLWidget* getPrimaryWidget() override { return nullptr; } virtual MainWindow* getPrimaryWindow() override { return nullptr; } virtual QOpenGLContext* getPrimaryContext() override { return nullptr; } - virtual ui::Menu* getPrimaryMenu() { return nullptr; } + virtual ui::Menu* getPrimaryMenu() override { return nullptr; } virtual bool isForeground() override { return true; } virtual const DisplayPluginPointer getActiveDisplayPlugin() const override { return DisplayPluginPointer(); } }; @@ -121,7 +121,7 @@ int main(int argc, char** argv) { }; foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { - inputPlugin->pluginUpdate(delta, calibrationData, false); + inputPlugin->pluginUpdate(delta, calibrationData); } auto userInputMapper = DependencyManager::get(); @@ -144,7 +144,7 @@ int main(int argc, char** argv) { if (name == KeyboardMouseDevice::NAME) { userInputMapper->registerDevice(std::dynamic_pointer_cast(inputPlugin)->getInputDevice()); } - inputPlugin->pluginUpdate(0, calibrationData, false); + inputPlugin->pluginUpdate(0, calibrationData); } rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface()); }