mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge branch 'master' into 20917
This commit is contained in:
commit
504516b627
102 changed files with 1577 additions and 1010 deletions
|
@ -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:
|
||||
|
|
|
@ -55,9 +55,9 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
if (message->getSize() == 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<ReceivedMessag
|
|||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(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<DomainServerNodeData*>(newNode->getLinkedData());
|
||||
|
||||
|
|
|
@ -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<ReceivedMessage> mess
|
|||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(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<LimitedNodeList>();
|
||||
|
@ -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<DomainServerNodeData*>(node->getLinkedData())->getUsername().isEmpty()) {
|
||||
++numConnectedAuthedUsers;
|
||||
static const QString DEFAULT_HOSTNAME = "*";
|
||||
|
||||
nodeList->eachNode([&numConnectedUnassigned, &userHostnames](const SharedNodePointer& node) {
|
||||
if (node->getLinkedData()) {
|
||||
auto nodeData = static_cast<DomainServerNodeData*>(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()));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
HifiSockAddr localSockAddr;
|
||||
HifiSockAddr senderSockAddr;
|
||||
QList<NodeType_t> interestList;
|
||||
QString placeName;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
@ -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<int> maxOctreePacketsPerSecond("maxOctreePPS", DEFAULT_MAX_OCTREE_PPS);
|
||||
|
||||
const QHash<QString, Application::AcceptURLMethod> 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<RenderableWebEntityItem*>(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<EntityEditPacketSender*>(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<KeyboardMouseDevice>(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<FramebufferCache>()->getPrimaryFramebuffer();
|
||||
|
||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
renderArgs._blitFramebuffer = DependencyManager::get<FramebufferCache>()->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<QEvent::Type>(Present)), Qt::LowEventPriority);
|
||||
isPainting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
idle();
|
||||
|
||||
postEvent(this, new QEvent(static_cast<QEvent::Type>(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<QEvent::Type>(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<NodeList>();
|
||||
|
||||
|
@ -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<OffscreenUi>();
|
||||
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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...";
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <SharedUtil.h>
|
||||
#include <TextRenderer3D.h>
|
||||
#include <TextureCache.h>
|
||||
#include <VariantMapToScriptValue.h>
|
||||
#include <DebugDraw.h>
|
||||
|
||||
#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.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
typedef std::shared_ptr<render::Item::PayloadInterface> PayloadPointer;
|
||||
|
||||
void init();
|
||||
void updateAvatarEntities();
|
||||
void simulate(float deltaTime);
|
||||
virtual void simulateAttachments(float deltaTime);
|
||||
|
||||
|
|
|
@ -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<ZoneEntityItem> 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<ZoneEntityItem> zone = entityTreeRenderer->myAvatarZone();
|
||||
if (zone) {
|
||||
ghostingAllowed = zone->getGhostingAllowed();
|
||||
}
|
||||
}
|
||||
bool checked = menu->isOptionChecked(MenuOption::EnableCharacterController);
|
||||
if (!ghostingAllowed) {
|
||||
checked = true;
|
||||
}
|
||||
|
||||
_characterController.setEnabled(checked);
|
||||
}
|
||||
|
||||
void MyAvatar::clearDriveKeys() {
|
||||
|
|
|
@ -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> 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<JointData> jointArray;
|
||||
|
@ -1628,9 +1656,69 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) {
|
|||
QVector<AttachmentData> 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<AvatarData*>(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<QUuid>::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<AvatarData*>(this), "getRecentlyDetachedIDs", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(AvatarEntityIDs, result));
|
||||
return result;
|
||||
}
|
||||
AvatarEntityIDs result = _avatarEntityDetached;
|
||||
_avatarEntityDetached.clear();
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,8 @@ typedef unsigned long long quint64;
|
|||
using AvatarSharedPointer = std::shared_ptr<AvatarData>;
|
||||
using AvatarWeakPointer = std::weak_ptr<AvatarData>;
|
||||
using AvatarHash = QHash<QUuid, AvatarSharedPointer>;
|
||||
using AvatarEntityMap = QMap<QUuid, QByteArray>;
|
||||
using AvatarEntityIDs = QSet<QUuid>;
|
||||
|
||||
using AvatarDataSequenceNumber = uint16_t;
|
||||
|
||||
|
@ -134,6 +136,10 @@ class AttachmentData;
|
|||
class Transform;
|
||||
using TransformPointer = std::shared_ptr<Transform>;
|
||||
|
||||
// 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;
|
||||
|
|
|
@ -107,21 +107,23 @@ void AvatarHashMap::processAvatarDataPacket(QSharedPointer<ReceivedMessage> mess
|
|||
}
|
||||
|
||||
void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage> 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> 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<ReceivedMessage>
|
|||
avatar->setAttachmentData(attachmentData);
|
||||
}
|
||||
|
||||
avatar->setAvatarEntityData(avatarEntityData);
|
||||
|
||||
if (avatar->getDisplayName() != displayName) {
|
||||
avatar->setDisplayName(displayName);
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -88,6 +88,8 @@ public:
|
|||
// For Scene.shouldRenderEntities
|
||||
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
||||
|
||||
std::shared_ptr<ZoneEntityItem> myAvatarZone() { return _bestZone; }
|
||||
|
||||
signals:
|
||||
void mousePressOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||
void mousePressOffEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event);
|
||||
|
|
|
@ -47,6 +47,9 @@ namespace render {
|
|||
}
|
||||
|
||||
void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
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<NodeList>();
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ enum class RenderItemStatusIcon {
|
|||
SIMULATION_OWNER = 3,
|
||||
HAS_ACTIONS = 4,
|
||||
OTHER_SIMULATION_OWNER = 5,
|
||||
CLIENT_ONLY = 6,
|
||||
NONE = 255
|
||||
};
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -981,7 +981,7 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() {
|
|||
PhysicalEntitySimulationPointer peSimulation = std::static_pointer_cast<PhysicalEntitySimulation>(simulation);
|
||||
EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr;
|
||||
if (packetSender) {
|
||||
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties);
|
||||
packetSender->queueEditEntityMessage(PacketType::EntityEdit, tree, entity->getID(), properties);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <QJsonDocument>
|
||||
#include <PerfStat.h>
|
||||
#include <OctalCode.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
@ -24,9 +25,7 @@ EntityEditPacketSender::EntityEditPacketSender() {
|
|||
}
|
||||
|
||||
void EntityEditPacketSender::processEntityEditNackPacket(QSharedPointer<ReceivedMessage> 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)) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <OctreeEditPacketSender.h>
|
||||
|
||||
#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<ReceivedMessage> message, SharedNodePointer sendingNode);
|
||||
void toggleNackPackets() { _shouldProcessNack = !_shouldProcessNack; }
|
||||
|
||||
private:
|
||||
bool _shouldProcessNack = true;
|
||||
AvatarData* _myAvatar { nullptr };
|
||||
QScriptEngine _scriptEngine;
|
||||
};
|
||||
#endif // hifi_EntityEditPacketSender_h
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<QUuid, quint64> _previouslyDeletedActions;
|
||||
|
||||
QUuid _sourceUUID; /// the server node UUID we came from
|
||||
|
||||
bool _clientOnly { false };
|
||||
QUuid _owningAvatarID;
|
||||
};
|
||||
|
||||
#endif // hifi_EntityItem_h
|
||||
|
|
|
@ -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<QString> 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);
|
||||
|
|
|
@ -205,6 +205,12 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector<bool>, QVector<bool>());
|
||||
DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector<glm::vec3>, QVector<glm::vec3>());
|
||||
|
||||
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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<NodeList>();
|
||||
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<NodeList>();
|
||||
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<NodeList>();
|
||||
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<NodeList>();
|
||||
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();
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<NodeList>();
|
||||
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<EntityTreeElement>(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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<controller::UserInputMapper>();
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<NodeList>()->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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<ReceivedMessage> mes
|
|||
|
||||
DependencyManager::get<NodeList>()->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 {
|
||||
|
|
|
@ -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<ReceivedMessage> 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;
|
||||
|
|
|
@ -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<AddressManager>()->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<AddressManager>()->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
|
||||
|
|
|
@ -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<PacketVersion>(AvatarMixerPacketVersion::SoftAttachmentSupport);
|
||||
return static_cast<PacketVersion>(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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <BulletCollision/CollisionShapes/btShapeHull.h>
|
||||
|
||||
#include <SharedUtil.h> // for MILLIMETERS_PER_METER
|
||||
|
||||
|
@ -64,6 +65,22 @@ btConvexHullShape* ShapeFactory::createConvexHull(const QVector<glm::vec3>& 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -100,10 +100,6 @@ bool Procedural::parseUrl(const QUrl& shaderUrl) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (_shaderUrl == shaderUrl) {
|
||||
return true;
|
||||
}
|
||||
|
||||
_shaderUrl = shaderUrl;
|
||||
_shaderDirty = true;
|
||||
|
||||
|
|
|
@ -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<model::Mesh::Part>();
|
||||
|
||||
batch.drawIndexed(model::Mesh::topologyToPrimitive(part._topology), part._numIndices, part._startIndex);
|
||||
{
|
||||
auto& part = mesh->getPartBuffer().get<model::Mesh::Part>(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<model::Mesh::Part> 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;
|
||||
}
|
||||
|
|
|
@ -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<ShapeKey> 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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "ScriptCache.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkAccessManager>
|
||||
|
@ -20,7 +22,7 @@
|
|||
#include <assert.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "ScriptCache.h"
|
||||
#include "ScriptEngines.h"
|
||||
#include "ScriptEngineLogging.h"
|
||||
|
||||
ScriptCache::ScriptCache(QObject* parent) {
|
||||
|
@ -78,22 +80,25 @@ void ScriptCache::scriptDownloaded() {
|
|||
QList<ScriptUser*> 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<ScriptEngines>()->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<ScriptEngines>()->isStopped()) {
|
||||
foreach(contentAvailableCallback thisCallback, allCallbacks) {
|
||||
thisCallback(url.toString(), scriptContent, true, success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<QUrl, ScriptEngine*> _scriptEnginesHash;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<glm::vec3>& newPoints) { _points << newPoints; }
|
||||
int getMaxNumPoints() const;
|
||||
|
||||
float computeVolume() const;
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
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 << " ]";
|
||||
}
|
||||
|
|
|
@ -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<QKeyEvent*>(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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -25,9 +25,7 @@ Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
|
|||
#define __OS_XUN__ 1
|
||||
#define BOOL int
|
||||
|
||||
#ifdef HAVE_NEURON
|
||||
#include <NeuronDataReader.h>
|
||||
#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<std::mutex> 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<controller::UserInputMapper>();
|
||||
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<controller::UserInputMapper>();
|
||||
|
@ -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<NeuronJoint> 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<NeuronPlugin::NeuronJoint>& joints, const std::vector<NeuronPlugin::NeuronJoint>& 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<NeuronPlugin::NeuronJoint>& joints, const std::vector<NeuronPlugin::NeuronJoint>& prevJoints);
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -15,10 +15,8 @@
|
|||
#include <qobject.h>
|
||||
#include <qvector.h>
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
#include <SDL.h>
|
||||
#undef main
|
||||
#endif
|
||||
|
||||
#include <controllers/InputDevice.h>
|
||||
#include <controllers/StandardControls.h>
|
||||
|
@ -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<Joystick>;
|
||||
|
@ -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
|
||||
|
|
|
@ -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<controller::UserInputMapper>();
|
||||
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<controller::UserInputMapper>();
|
||||
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<controller::UserInputMapper>();
|
||||
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
|
||||
}
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
#ifndef hifi__SDL2Manager_h
|
||||
#define hifi__SDL2Manager_h
|
||||
|
||||
#ifdef HAVE_SDL2
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
#include <controllers/UserInputMapper.h>
|
||||
#include <input-plugins/InputPlugin.h>
|
||||
|
@ -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<SDL_JoystickID, Joystick::Pointer> _openJoysticks;
|
||||
#endif
|
||||
bool _isInitialized;
|
||||
bool _isInitialized { false } ;
|
||||
static const QString NAME;
|
||||
};
|
||||
|
||||
|
|
|
@ -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<controller::UserInputMapper>();
|
||||
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();
|
||||
|
|
|
@ -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);
|
||||
|
|
18
plugins/hifiSpacemouse/CMakeLists.txt
Normal file
18
plugins/hifiSpacemouse/CMakeLists.txt
Normal file
|
@ -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()
|
|
@ -11,19 +11,75 @@
|
|||
|
||||
#include "SpacemouseManager.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <VersionHelpers.h>
|
||||
#endif
|
||||
|
||||
#include <UserActivityLogger.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <controllers/UserInputMapper.h>
|
||||
|
||||
#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<SpacemouseDevice> instance = std::make_shared<SpacemouseDevice>();
|
||||
#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<SpacemouseDevice> instance;
|
||||
|
||||
bool SpacemouseManager::isSupported() const {
|
||||
return Is3dmouseAttached();
|
||||
}
|
||||
|
||||
bool SpacemouseManager::activate() {
|
||||
fLast3dmouseInputTime = 0;
|
||||
|
||||
InitializeRawInput(GetActiveWindow());
|
||||
|
||||
QAbstractEventDispatcher::instance()->installNativeEventFilter(this);
|
||||
|
||||
if (!instance) {
|
||||
instance = std::make_shared<SpacemouseDevice>();
|
||||
}
|
||||
|
||||
if (instance->getDeviceID() == controller::Input::INVALID_DEVICE) {
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
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>();
|
||||
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 <VersionHelpers.h>
|
||||
|
||||
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>();
|
||||
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> rawInputDeviceList(nDevices);
|
||||
if (::GetRawInputDeviceList(&rawInputDeviceList[0], &nDevices, sizeof(RAWINPUTDEVICELIST)) == static_cast<unsigned int>(-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>();
|
||||
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<float>& 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> rawInputDeviceList(nDevices);
|
||||
if (::GetRawInputDeviceList(&rawInputDeviceList[0], &nDevices, sizeof(RAWINPUTDEVICELIST)) == static_cast<unsigned int>(-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
|
|
@ -17,22 +17,8 @@
|
|||
#include <controllers/InputDevice.h>
|
||||
#include <controllers/StandardControls.h>
|
||||
|
||||
#include "InputPlugin.h"
|
||||
#include <plugins/InputPlugin.h>
|
||||
|
||||
#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<float>& 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<float>& 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<float>& 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;
|
45
plugins/hifiSpacemouse/src/SpacemouseProvider.cpp
Normal file
45
plugins/hifiSpacemouse/src/SpacemouseProvider.cpp
Normal file
|
@ -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 <mutex>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QtPlugin>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
#include <plugins/RuntimePlugin.h>
|
||||
#include <plugins/InputPlugin.h>
|
||||
|
||||
#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"
|
1
plugins/hifiSpacemouse/src/plugin.json
Normal file
1
plugins/hifiSpacemouse/src/plugin.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
#include <QMainWindow>
|
||||
#include <QLoggingCategory>
|
||||
#include <QGLWidget>
|
||||
|
@ -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::Hmd_Eye, 2> VR_EYES { { vr::Eye_Left, vr::Eye_Right } };
|
||||
|
||||
bool OpenVrDisplayPlugin::isSupported() const {
|
||||
return (enableDebugOpenVR || !isOculusPresent()) && vr::VR_IsHmdPresent();
|
||||
return openVrSupported();
|
||||
}
|
||||
|
||||
bool OpenVrDisplayPlugin::internalActivate() {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QProcessEnvironment>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
|
@ -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) {
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
bool isOculusPresent();
|
||||
bool openVrSupported();
|
||||
|
||||
vr::IVRSystem* acquireOpenVrSystem();
|
||||
void releaseOpenVrSystem();
|
||||
|
||||
|
|
|
@ -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<controller::UserInputMapper>();
|
||||
|
||||
// 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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
90
scripts/system/assets/images/lock.svg
Normal file
90
scripts/system/assets/images/lock.svg
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64mm"
|
||||
height="64mm"
|
||||
viewBox="0 0 226.77165 226.77165"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="lock.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient5587"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5589" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5587"
|
||||
id="linearGradient5591"
|
||||
x1="63.214287"
|
||||
y1="970.93364"
|
||||
x2="165.35715"
|
||||
y2="970.93364"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="-342.3833"
|
||||
inkscape:cy="122.87157"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2782"
|
||||
inkscape:window-height="1764"
|
||||
inkscape:window-x="98"
|
||||
inkscape:window-y="36"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-825.59055)">
|
||||
<rect
|
||||
style="fill:url(#linearGradient5591);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect4138"
|
||||
width="97.14286"
|
||||
height="82.85714"
|
||||
x="65.714287"
|
||||
y="929.50507" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#a7abdf;stroke-width:9.00350285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 78.319686,927.59356 c -2.221784,0.87707 -0.135759,-31.35492 36.848474,-30.3048 34.29837,0.97388 35.8249,30.70886 35.8249,30.70886"
|
||||
id="path4148"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="csc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
90
scripts/system/assets/images/unlock.svg
Normal file
90
scripts/system/assets/images/unlock.svg
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64mm"
|
||||
height="64mm"
|
||||
viewBox="0 0 226.77165 226.77165"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="unlock.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient4135"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4137" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4135"
|
||||
id="linearGradient4139"
|
||||
x1="63.214287"
|
||||
y1="970.93364"
|
||||
x2="165.35715"
|
||||
y2="970.93364"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.979899"
|
||||
inkscape:cx="-280.07997"
|
||||
inkscape:cy="122.87157"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2782"
|
||||
inkscape:window-height="1764"
|
||||
inkscape:window-x="98"
|
||||
inkscape:window-y="36"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-825.59055)">
|
||||
<rect
|
||||
style="fill:url(#linearGradient4139);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect4138"
|
||||
width="97.14286"
|
||||
height="82.85714"
|
||||
x="65.714287"
|
||||
y="929.50507" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#abaac5;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 142.93659,923.06268 c -2.19267,1.09633 -0.13398,-39.19338 36.36549,-37.88073 33.84882,1.21733 35.35534,38.3858 35.35534,38.3858"
|
||||
id="path4148"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="csc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 @@
|
|||
<input type="text" id="property-zone-skybox-url">
|
||||
</div>
|
||||
|
||||
<div class="zone-group zone-section property checkbox">
|
||||
<input type="checkbox" id="property-zone-flying-allowed">
|
||||
<label for="property-zone-flying-allowed"> Flying Allowed</label>
|
||||
</div>
|
||||
<div class="zone-group zone-section property checkbox">
|
||||
<input type="checkbox" id="property-zone-ghosting-allowed">
|
||||
<label for="property-zone-ghosting-allowed"> Ghosting Allowed</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section-header web-group web-section">
|
||||
<label>Web</label><span>M</span>
|
||||
|
|
|
@ -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;
|
||||
ToolBar.HORIZONTAL = 1;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue