diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 9b3d2ed24e..82e88f67ef 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -12,7 +12,6 @@ #include #include - #include "ScriptableAvatar.h" // hold and priority unused but kept so that client side JS can run. @@ -48,14 +47,12 @@ AnimationDetails ScriptableAvatar::getAnimationDetails() { } void ScriptableAvatar::update(float deltatime) { + if (_bind.isNull() && !_skeletonFBXURL.isEmpty()) { // AvatarData will parse the .fst, but not get the .fbx skeleton. + _bind = DependencyManager::get()->getAnimation(_skeletonFBXURL); + } + // Run animation - if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0) { - QStringList modelJoints = getJointNames(); - QStringList animationJoints = _animation->getJointNames(); - - if (_jointData.size() != modelJoints.size()) { - _jointData.resize(modelJoints.size()); - } + if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && _bind->isLoaded()) { float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps; if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) { @@ -63,19 +60,29 @@ void ScriptableAvatar::update(float deltatime) { currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame); } _animationDetails.currentFrame = currentFrame; + + const QVector& modelJoints = _bind->getGeometry().joints; + QStringList animationJointNames = _animation->getJointNames(); + + if (_jointData.size() != modelJoints.size()) { + _jointData.resize(modelJoints.size()); + } const int frameCount = _animation->getFrames().size(); const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount); const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount); const float frameFraction = glm::fract(currentFrame); - for (int i = 0; i < animationJoints.size(); i++) { - const QString& name = animationJoints[i]; - int mapping = getJointIndex(name); + for (int i = 0; i < animationJointNames.size(); i++) { + const QString& name = animationJointNames[i]; + // As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than + // trusting the .fst (which is sometimes not updated to match changes to .fbx). + int mapping = _bind->getGeometry().getJointIndex(name); if (mapping != -1 && !_maskedJoints.contains(name)) { JointData& data = _jointData[mapping]; - auto newRotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); + auto newRotation = modelJoints[mapping].preRotation * + safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); // We could probably do translations as in interpolation in model space (rather than the parent space that each frame is in), // but we don't do so for MyAvatar yet, so let's not be different here. if (data.rotation != newRotation) { diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 78b2be1057..30c48d02bf 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -33,6 +33,7 @@ private: AnimationPointer _animation; AnimationDetails _animationDetails; QStringList _maskedJoints; + AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies }; #endif // hifi_ScriptableAvatar_h \ No newline at end of file diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 0cdc7f9921..e22f241453 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -43,6 +43,7 @@ void OctreeInboundPacketProcessor::resetStats() { _totalPackets = 0; _lastNackTime = usecTimestampNow(); + QWriteLocker locker(&_senderStatsLock); _singleSenderStats.clear(); } @@ -220,6 +221,8 @@ void OctreeInboundPacketProcessor::trackInboundPacket(const QUuid& nodeUUID, uns _totalElementsInPacket += editsInPacket; _totalPackets++; + QWriteLocker locker(&_senderStatsLock); + // find the individual senders stats and track them there too... // see if this is the first we've heard of this node... if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) { @@ -242,6 +245,8 @@ int OctreeInboundPacketProcessor::sendNackPackets() { int packetsSent = 0; int totalBytesSent = 0; + QWriteLocker locker(&_senderStatsLock); + NodeToSenderStatsMapIterator i = _singleSenderStats.begin(); while (i != _singleSenderStats.end()) { @@ -262,10 +267,9 @@ int OctreeInboundPacketProcessor::sendNackPackets() { } const SharedNodePointer& destinationNode = DependencyManager::get()->nodeWithUUID(nodeUUID); - // If the node no longer exists, wait until the ReceivedPacketProcessor has cleaned up the node - // to remove it from our stats list. - // FIXME Is it safe to clean it up here before ReceivedPacketProcess has? + // if the node no longer exists, remove its stats if (!destinationNode) { + i = _singleSenderStats.erase(i); continue; } diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.h b/assignment-client/src/octree/OctreeInboundPacketProcessor.h index 3ddb76b3fa..83960abaa6 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.h +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.h @@ -72,7 +72,7 @@ public: void resetStats(); - NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; } + NodeToSenderStatsMap getSingleSenderStats() { QReadLocker locker(&_senderStatsLock); return _singleSenderStats; } virtual void terminating() { _shuttingDown = true; ReceivedPacketProcessor::terminating(); } @@ -94,15 +94,16 @@ private: OctreeServer* _myServer; int _receivedPacketCount; - quint64 _totalTransitTime; - quint64 _totalProcessTime; - quint64 _totalLockWaitTime; - quint64 _totalElementsInPacket; - quint64 _totalPackets; + std::atomic _totalTransitTime; + std::atomic _totalProcessTime; + std::atomic _totalLockWaitTime; + std::atomic _totalElementsInPacket; + std::atomic _totalPackets; NodeToSenderStatsMap _singleSenderStats; + QReadWriteLock _senderStatsLock; - quint64 _lastNackTime; + std::atomic _lastNackTime; bool _shuttingDown; }; #endif // hifi_OctreeInboundPacketProcessor_h diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 297f872108..7cd3e59edf 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -711,7 +711,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url int senderNumber = 0; - NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats(); + NodeToSenderStatsMap allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats(); for (NodeToSenderStatsMapConstIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) { senderNumber++; QUuid senderID = i.key(); diff --git a/examples/acScripts/animatedAvatarAgent.js b/examples/acScripts/animatedAvatarAgent.js index 3e5c90ed1a..74b1032328 100644 --- a/examples/acScripts/animatedAvatarAgent.js +++ b/examples/acScripts/animatedAvatarAgent.js @@ -1,6 +1,6 @@ "use strict"; /*jslint vars: true, plusplus: true*/ -/*global Agent, Avatar, Script, Entities, Vec3, print*/ +/*global Agent, Avatar, Script, Entities, Vec3, Quat, print*/ // // animatedAvatar.js // examples/acScripts @@ -16,15 +16,62 @@ var origin = {x: 500, y: 500, z: 500}; var spread = 20; // meters +var turnSpread = 90; // How many degrees should turn from front range over. var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35}; -Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy -Avatar.displayName = "'Bot"; +var models = [ // Commented-out avatars do not animate properly on AC's. Presumably because ScriptableAvatar doesn't use model pre-rotations. + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/alan/alan.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/andrew/andrew.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/austin/austin.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/birarda/birarda.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/brad/brad.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/chris/chris2.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/clement/clement.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/emily/emily.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/ewing/ewing.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/howard/howard.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/huffman/huffman.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/james/james.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/philip/philip.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/ryan/ryan.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/sam/sam.fst", + "https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/tony/tony.fst", + + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/1e57c395-612e-4acd-9561-e79dbda0bc49/faafb83c63a3e5e265884d270fc29f8b.fst", + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/615ca447-06ee-4215-8dd1-a609c2fcd0cd/c7af6d4224c501fdd9cb54e0101ff281.fst", + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/731c39d7-559a-4ce2-947c-3e2768f5471c/8d5eba2fd5bf068259556aec1861c5dd.fst", + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/8bdaa1ec-99df-4a29-a249-6941c7fd1930/37351a18a3dea468088fc3d822aaf29c.fst", + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/0909d7b7-c67e-45fb-acd9-a07380dc6b9c/da76b8c59dbc680bdda90df4b9a46faa.fst", + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/ad0dffd7-f811-487b-a20a-2509235491ef/29106da1f7e6a42c7907603421fd7df5.fst", + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/3ebe5c84-8b88-4d91-86ac-f104f3620fe3/3534b032d079514aa8960a316500ce13.fst", + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst", + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/ff060580-2fc7-4b6c-8e12-f023d05363cf/dadef29b1e60f23b413d1850d7e0dd4a.fst", + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/b55d3baf-4eb3-4cac-af4c-0fb66d0c907b/ad2c9157f3924ab1f7f6ea87a8cce6cc.fst", + "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/d029ae8d-2905-4eb7-ba46-4bd1b8cb9d73/4618d52e711fbb34df442b414da767bb.fst" +]; +var nameMap = { + faafb83c63a3e5e265884d270fc29f8b: 'albert', + c7af6d4224c501fdd9cb54e0101ff281: 'boss', + '8d5eba2fd5bf068259556aec1861c5dd': 'dougland', + '37351a18a3dea468088fc3d822aaf29c': 'fightbot blue', + da76b8c59dbc680bdda90df4b9a46faa: 'judd', + '29106da1f7e6a42c7907603421fd7df5': 'kate', + '3534b032d079514aa8960a316500ce13': 'lenny', + '98baa90b3b66803c5d7bd4537fca6993': 'lovejoy', + dadef29b1e60f23b413d1850d7e0dd4a: 'mery', // eyes no good + ad2c9157f3924ab1f7f6ea87a8cce6cc: 'owen', + '4618d52e711fbb34df442b414da767bb': 'will' +}; + +Avatar.skeletonModelURL = models[Math.round(Math.random() * (models.length - 1))]; // pick one +Avatar.displayName = Avatar.skeletonModelURL.match(/\/(\w*).fst/)[1]; // grab the file basename +Avatar.displayName = nameMap[Avatar.displayName] || Avatar.displayName; var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start. Agent.isAvatar = true; function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2. Script.setTimeout(function () { Avatar.position = Vec3.sum(origin, {x: coord(), y: 0, z: coord()}); + Avatar.orientation = Quat.fromPitchYawRollDegrees(0, turnSpread * (Math.random() - 0.5), 0); print("Starting at", JSON.stringify(Avatar.position)); Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame); }, millisecondsToWaitBeforeStarting); diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 4f0a5880e0..ebfa248f58 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -200,6 +200,7 @@ function entityIsGrabbedByOther(entityID) { return false; } + function MyController(hand) { this.hand = hand; if (this.hand === RIGHT_HAND) { @@ -223,6 +224,8 @@ function MyController(hand) { this.rawTriggerValue = 0; this.rawBumperValue = 0; + this.overlayLine = null; + this.offsetPosition = { x: 0.0, y: 0.0, @@ -318,6 +321,33 @@ function MyController(hand) { }); } + this.overlayLineOn = function(closePoint, farPoint, color) { + if (this.overlayLine === null) { + var lineProperties = { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + ignoreRayIntersection: true, // always ignore this + visible: true, + alpha: 1 + }; + + this.overlayLine = Overlays.addOverlay("line3d", lineProperties); + + } else { + var success = Overlays.editOverlay(this.overlayLine, { + lineWidth: 5, + start: closePoint, + end: farPoint, + color: color, + visible: true, + ignoreRayIntersection: true, // always ignore this + alpha: 1 + }); + } + } + this.lineOn = function(closePoint, farPoint, color) { // draw a line if (this.pointer === null) { @@ -356,6 +386,13 @@ function MyController(hand) { this.pointer = null; }; + this.overlayLineOff = function() { + if (this.overlayLine !== null) { + Overlays.deleteOverlay(this.overlayLine); + } + this.overlayLine = null; + }; + this.triggerPress = function(value) { _this.rawTriggerValue = value; }; @@ -604,7 +641,8 @@ function MyController(hand) { } } - this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + //this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR); }; this.distanceHolding = function() { @@ -650,6 +688,9 @@ function MyController(hand) { this.currentAvatarPosition = MyAvatar.position; this.currentAvatarOrientation = MyAvatar.orientation; + this.overlayLineOff(); + + }; this.continueDistanceHolding = function() { @@ -757,6 +798,7 @@ function MyController(hand) { } this.lineOff(); + this.overlayLineOff(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); this.activateEntity(this.grabbedEntity, grabbedProperties); @@ -898,6 +940,7 @@ function MyController(hand) { this.pullTowardEquipPosition = function() { this.lineOff(); + this.overlayLineOff(); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); @@ -1101,7 +1144,7 @@ function MyController(hand) { this.release = function() { this.lineOff(); - + this.overlayLineOff(); if (this.grabbedEntity !== null) { if (this.actionID !== null) { Entities.deleteAction(this.grabbedEntity, this.actionID); @@ -1223,10 +1266,10 @@ Controller.enableMapping(MAPPING_NAME); var handToDisable = 'none'; function update() { - if (handToDisable !== LEFT_HAND) { + if (handToDisable !== LEFT_HAND && handToDisable!=='both') { leftController.update(); } - if (handToDisable !== RIGHT_HAND) { + if (handToDisable !== RIGHT_HAND && handToDisable!=='both') { rightController.update(); } } @@ -1234,15 +1277,20 @@ function update() { Messages.subscribe('Hifi-Hand-Disabler'); handleHandDisablerMessages = function(channel, message, sender) { - + if (sender === MyAvatar.sessionUUID) { - handToDisable = message; if (message === 'left') { handToDisable = LEFT_HAND; } if (message === 'right') { handToDisable = RIGHT_HAND; } + if(message==='both'){ + handToDisable='both'; + } + if(message==='none'){ + handToDisable='none'; + } } } diff --git a/examples/controllers/leap/laserPointer.js b/examples/controllers/leap/laserPointer.js index 156e9ba298..c7d72e6cff 100644 --- a/examples/controllers/leap/laserPointer.js +++ b/examples/controllers/leap/laserPointer.js @@ -22,8 +22,10 @@ var laserPointer = (function () { ]; function isHandPointing(hand) { - var MINIMUM_TRIGGER_PULL = 0.9; - return Controller.getTriggerValue(hand) > MINIMUM_TRIGGER_PULL; + var MINIMUM_TRIGGER_PULL = 0.9, + controller; + controller = hand === 0 ? Controller.Standard.LT : Controller.Standard.RT; + return Controller.getValue(controller) > MINIMUM_TRIGGER_PULL; } function isFingerPointing(hand) { diff --git a/examples/controllers/leap/leapHands.js b/examples/controllers/leap/leapHands.js index 6bdca051be..7835df7452 100644 --- a/examples/controllers/leap/leapHands.js +++ b/examples/controllers/leap/leapHands.js @@ -193,13 +193,12 @@ var leapHands = (function () { } // Set avatar arms vertical, forearms horizontal, as "zero" position for calibration - MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, -90.0)); - MyAvatar.setJointData("LeftForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0)); - MyAvatar.setJointData("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0)); - MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 90.0)); - MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0)); - MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0)); - + MyAvatar.setJointRotation("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0)); + MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 90.0, 90.0)); + MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0)); + MyAvatar.setJointRotation("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0)); + MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, -90.0, -90.0)); + MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0)); // Wait for arms to assume their positions before calculating Script.setTimeout(finishCalibration, CALIBRATION_TIME); @@ -382,23 +381,14 @@ var leapHands = (function () { // Hand rotation in camera coordinates ... handRotation = { - x: handRotation.z, - y: handRotation.y, - z: handRotation.x, + x: -handRotation.x, + y: -handRotation.z, + z: -handRotation.y, w: handRotation.w }; // Hand rotation in avatar coordinates ... - if (h === 0) { - handRotation.x = -handRotation.x; - handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation); - handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 }), handRotation); - } else { - handRotation.z = -handRotation.z; - handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation); - handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 0, z: 1 }), handRotation); - } - + handRotation = Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), handRotation); cameraOrientation.x = -cameraOrientation.x; cameraOrientation.z = -cameraOrientation.z; handRotation = Quat.multiply(cameraOrientation, handRotation); @@ -421,22 +411,14 @@ var leapHands = (function () { // Hand rotation in camera coordinates ... handRotation = { - x: handRotation.z, - y: handRotation.y, - z: handRotation.x, + x: -handRotation.x, + y: -handRotation.z, + z: -handRotation.y, w: handRotation.w }; // Hand rotation in avatar coordinates ... - if (h === 0) { - handRotation.x = -handRotation.x; - handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }), - handRotation); - } else { - handRotation.z = -handRotation.z; - handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }), - handRotation); - } + handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation); } // Set hand position and orientation ... @@ -462,7 +444,7 @@ var leapHands = (function () { w: locRotation.w }; } - MyAvatar.setJointData(fingers[h][i][j].jointName, locRotation); + MyAvatar.setJointRotation(fingers[h][i][j].jointName, locRotation); } } } diff --git a/examples/example/games/exterminatorGame/pistol.js b/examples/example/games/exterminatorGame/pistol.js index 365e29fbe5..e1923835c3 100644 --- a/examples/example/games/exterminatorGame/pistol.js +++ b/examples/example/games/exterminatorGame/pistol.js @@ -18,6 +18,7 @@ Script.include("../../../libraries/constants.js"); var GUN_FORCE =20; +Messages.sendMessage('Hifi-Hand-Disabler', "both"); var gameName = "Kill All The Rats!" // var HOST = "localhost:5000" @@ -194,6 +195,7 @@ function fire(side, value) { function scriptEnding() { + Messages.sendMessage('Hifi-Hand-Disabler', 'none'); mapping.disable(); for (var i = 0; i < pointers.length; ++i) { Overlays.deleteOverlay(pointers[i]); diff --git a/examples/toybox/doll/doll.js b/examples/toybox/doll/doll.js index 577f86cae2..04712f0e1d 100644 --- a/examples/toybox/doll/doll.js +++ b/examples/toybox/doll/doll.js @@ -36,14 +36,10 @@ Entities.editEntity(this.entityID, { animation: { url: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx", - currentFrame: 0 + running: true } }); - Entities.editEntity(_this.entityID, { - animationIsPlaying: true - }); - var position = Entities.getEntityProperties(this.entityID, "position").position; this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], { position: position, @@ -67,8 +63,10 @@ this.audioInjector.stop(); Entities.editEntity(this.entityID, { animation: { - url: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx", - currentFrame: 0 + // Providing actual model fbx for animation used to work, now contorts doll into a weird ball + // See bug: https://app.asana.com/0/26225263936266/70097355490098 + // url: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx", + running: false, } }); diff --git a/examples/toybox/pistol/pistol.js b/examples/toybox/pistol/pistol.js index 94c4ce039f..8ef26b94c1 100644 --- a/examples/toybox/pistol/pistol.js +++ b/examples/toybox/pistol/pistol.js @@ -22,6 +22,8 @@ Controller.Standard.LT, Controller.Standard.RT, ]; + var RELOAD_THRESHOLD = 0.95; + Pistol = function() { _this = this; this.equipped = false; @@ -36,7 +38,7 @@ this.fireSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/GUN-SHOT2.raw"); this.ricochetSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/Ricochet.L.wav"); this.playRichochetSoundChance = 0.1; - this.fireVolume = 0.5; + this.fireVolume = 0.2; this.bulletForce = 10; @@ -45,6 +47,7 @@ }; Pistol.prototype = { + canShoot: false, startEquip: function(id, params) { this.equipped = true; @@ -62,6 +65,17 @@ }, toggleWithTriggerPressure: function() { this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]); + + if (this.triggerValue < RELOAD_THRESHOLD) { + // print('RELOAD'); + this.canShoot = true; + } + if (this.canShoot === true && this.triggerValue === 1) { + // print('SHOOT'); + this.fire(); + this.canShoot = false; + } + if (this.triggerValue < DISABLE_LASER_THRESHOLD && this.showLaser === true) { this.showLaser = false; Overlays.editOverlay(this.laser, { @@ -74,6 +88,7 @@ }); } + }, updateLaser: function() { var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']); @@ -101,7 +116,7 @@ preload: function(entityID) { this.entityID = entityID; - this.initControllerMapping(); + // this.initControllerMapping(); this.laser = Overlays.addOverlay("line3d", { start: ZERO_VECTOR, end: ZERO_VECTOR, @@ -150,22 +165,7 @@ } }, - initControllerMapping: function() { - this.mapping = Controller.newMapping(); - this.mapping.from(Controller.Standard.LT).hysteresis(0.0, 0.75).to(function(value) { - _this.triggerPress(0, value); - }); - - - this.mapping.from(Controller.Standard.RT).hysteresis(0.0, 0.75).to(function(value) { - _this.triggerPress(1, value); - }); - this.mapping.enable(); - - }, - unload: function() { - this.mapping.disable(); Overlays.deleteOverlay(this.laser); }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5ac14ac0ec..c0315c44e3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2856,6 +2856,8 @@ void Application::update(float deltaTime) { } } + _controllerScriptingInterface->updateInputControllers(); + // Transfer the user inputs to the driveKeys // FIXME can we drop drive keys and just have the avatar read the action states directly? myAvatar->clearDriveKeys(); diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 547f16ea8b..11053154b6 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -84,13 +84,12 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const { return qApp->getUiSize(); } -controller::InputController::Pointer ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { - // This is where we retreive the Device Tracker category and then the sub tracker within it +controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { + // This is where we retrieve the Device Tracker category and then the sub tracker within it auto icIt = _inputControllers.find(0); if (icIt != _inputControllers.end()) { - return (*icIt).second; - } - + return (*icIt).second.get(); + } // Look for device DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString()); @@ -110,18 +109,24 @@ controller::InputController::Pointer ControllerScriptingInterface::createInputCo controller::InputController::Pointer inputController = std::make_shared(deviceID, trackerID, this); controller::InputController::Key key = inputController->getKey(); _inputControllers.insert(InputControllerMap::value_type(key, inputController)); - return inputController; + return inputController.get(); } } } - return controller::InputController::Pointer(); + return nullptr; } -void ControllerScriptingInterface::releaseInputController(controller::InputController::Pointer input) { +void ControllerScriptingInterface::releaseInputController(controller::InputController* input) { _inputControllers.erase(input->getKey()); } +void ControllerScriptingInterface::updateInputControllers() { + for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) { + (*it).second->update(); + } +} + InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : _deviceTrackerId(deviceTrackerId), _subTrackerId(subTrackerId), diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 4c69551dd2..8bd698cfb2 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -85,6 +85,8 @@ public: bool isKeyCaptured(const KeyEvent& event) const; bool isJoystickCaptured(int joystickIndex) const; + void updateInputControllers(); + public slots: virtual void captureKeyEvents(const KeyEvent& event); @@ -96,8 +98,8 @@ public slots: virtual glm::vec2 getViewportDimensions() const; /// Factory to create an InputController - virtual controller::InputController::Pointer createInputController(const QString& deviceName, const QString& tracker); - virtual void releaseInputController(controller::InputController::Pointer input); + virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker); + virtual void releaseInputController(controller::InputController* input); signals: void keyPressEvent(const KeyEvent& event); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 0f588b5013..597c4c5986 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -116,7 +116,7 @@ void AvatarData::setOrientation(const glm::quat& orientation, bool overideRefere glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(orientation)); _bodyPitch = eulerAngles.x; _bodyYaw = eulerAngles.y; - _bodyRoll = eulerAngles.z; + _bodyRoll = eulerAngles.z; } } @@ -1212,7 +1212,14 @@ void AvatarData::setJointMappingsFromNetworkReply() { QByteArray line; while (!(line = networkReply->readLine()).isEmpty()) { - if (!(line = line.trimmed()).startsWith("jointIndex")) { + line = line.trimmed(); + if (line.startsWith("filename")) { + int filenameIndex = line.indexOf('=') + 1; + if (filenameIndex > 0) { + _skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed())); + } + } + if (!line.startsWith("jointIndex")) { continue; } int jointNameIndex = line.indexOf('=') + 1; @@ -1522,6 +1529,17 @@ QJsonObject AvatarData::toJson() const { } void AvatarData::fromJson(const QJsonObject& json) { + // The head setOrientation likes to overwrite the avatar orientation, + // so lets do the head first + // Most head data is relative to the avatar, and needs no basis correction, + // but the lookat vector does need correction + if (json.contains(JSON_AVATAR_HEAD)) { + if (!_headData) { + _headData = new HeadData(this); + } + _headData->fromJson(json[JSON_AVATAR_HEAD].toObject()); + } + if (json.contains(JSON_AVATAR_HEAD_MODEL)) { auto faceModelURL = json[JSON_AVATAR_HEAD_MODEL].toString(); if (faceModelURL != getFaceModelURL().toString()) { @@ -1593,15 +1611,6 @@ void AvatarData::fromJson(const QJsonObject& json) { } setRawJointData(jointArray); } - - // Most head data is relative to the avatar, and needs no basis correction, - // but the lookat vector does need correction - if (json.contains(JSON_AVATAR_HEAD)) { - if (!_headData) { - _headData = new HeadData(this); - } - _headData->fromJson(json[JSON_AVATAR_HEAD].toObject()); - } } // Every frame will store both a basis for the recording and a relative transform diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index bc71bc52cd..2d5a395e2a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -392,6 +392,7 @@ protected: QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit + QUrl _skeletonFBXURL; QVector _attachmentData; QString _displayName; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 995a92bf83..49009a3ad2 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -81,6 +81,9 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { out = qobject_cast(object.toQObject()); } +Q_DECLARE_METATYPE(controller::InputController*) +static int inputControllerPointerId = qRegisterMetaType(); + QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) { return engine->newQObject(in); } @@ -89,8 +92,6 @@ void inputControllerFromScriptValue(const QScriptValue &object, controller::Inpu out = qobject_cast(object.toQObject()); } - - static bool hasCorrectSyntax(const QScriptProgram& program) { const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 459a6c5765..982c65fc08 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -132,11 +132,12 @@ createLights(); createCat({ - x: 551.09, - y: 494.98, - z: 503.49 + x: 551.0, + y: 495.3, + z: 503.3 }); + createSprayCan({ x: 549.7, y: 495.6, @@ -240,6 +241,7 @@ gravity: BOW_GRAVITY, shapeType: 'compound', compoundShapeURL: COLLISION_HULL_URL, + collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/bow_fall.L.wav", script: bowScriptURL, userData: JSON.stringify({ resetMe: { @@ -648,6 +650,7 @@ z: 0.08 }, collisionsWillMove: true, + collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/flashlight_drop.L.wav", gravity: { x: 0, y: -3.5, @@ -1184,10 +1187,11 @@ z: 0.07 }, collisionsWillMove: true, + collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/SpryPntCnDrp1.L.wav", shapeType: 'box', gravity: { x: 0, - y: -0.5, + y: -3.0, z: 0 }, velocity: { diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index a37a8701bb..6e89e12d41 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -110,9 +110,9 @@ MasterReset = function() { createCat({ - x: 551.09, - y: 494.98, - z: 503.49 + x: 551.0, + y: 495.3, + z: 503.3 }); createSprayCan({ @@ -220,6 +220,7 @@ MasterReset = function() { gravity: BOW_GRAVITY, shapeType: 'compound', compoundShapeURL: COLLISION_HULL_URL, + collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/bow_fall.L.wav", script: bowScriptURL, userData: JSON.stringify({ resetMe: { @@ -629,6 +630,7 @@ MasterReset = function() { z: 0.08 }, collisionsWillMove: true, + collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/flashlight_drop.L.wav", gravity: { x: 0, y: -3.5, @@ -1165,10 +1167,11 @@ MasterReset = function() { z: 0.07 }, collisionsWillMove: true, + collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/SpryPntCnDrp1.L.wav", shapeType: 'box', gravity: { x: 0, - y: -0.5, + y: -3.0, z: 0 }, velocity: {