diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 157154606f..d9109703cb 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -233,8 +233,8 @@ void Agent::setIsAvatar(bool isAvatar) { } if (_avatarBillboardTimer) { - _avatarIdentityTimer->stop(); - delete _avatarIdentityTimer; + _avatarBillboardTimer->stop(); + delete _avatarBillboardTimer; _avatarBillboardTimer = nullptr; } } @@ -364,10 +364,15 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) { } void Agent::aboutToFinish() { - _scriptEngine->stop(); + setIsAvatar(false);// will stop timers for sending billboards and identity packets + if (_scriptEngine) { + _scriptEngine->stop(); + } - _pingTimer->stop(); - delete _pingTimer; + if (_pingTimer) { + _pingTimer->stop(); + delete _pingTimer; + } // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(NULL); diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 4ae07042e8..cb94990037 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -238,7 +238,6 @@ int OctreeInboundPacketProcessor::sendNackPackets() { return 0; } - auto nackPacketList = NLPacketList::create(_myServer->getMyEditNackType()); auto nodeList = DependencyManager::get(); int packetsSent = 0; @@ -272,18 +271,19 @@ int OctreeInboundPacketProcessor::sendNackPackets() { auto it = missingSequenceNumbers.constBegin(); - while (it != missingSequenceNumbers.constEnd()) { - unsigned short int sequenceNumber = *it; - nackPacketList->writePrimitive(sequenceNumber); - ++it; - } - - - if (nackPacketList->getNumPackets()) { + if (it != missingSequenceNumbers.constEnd()) { + auto nackPacketList = NLPacketList::create(_myServer->getMyEditNackType()); + + while (it != missingSequenceNumbers.constEnd()) { + unsigned short int sequenceNumber = *it; + nackPacketList->writePrimitive(sequenceNumber); + ++it; + } + qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID; - + packetsSent += nackPacketList->getNumPackets(); - + // send the list of nack packets nodeList->sendPacketList(std::move(nackPacketList), *destinationNode); } diff --git a/examples/actionInspector.js b/examples/actionInspector.js new file mode 100644 index 0000000000..934120ddf6 --- /dev/null +++ b/examples/actionInspector.js @@ -0,0 +1,146 @@ +// +// actionInspector.js +// examples +// +// Created by Seth Alves on 2015-9-30. +// 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 +// + +Script.include("libraries/utils.js"); + + +var INSPECT_RADIUS = 10; +var overlays = {}; + + +var toType = function(obj) { + return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() +} + + +function actionArgumentsToString(actionArguments) { + var result = "type: " + actionArguments["type"] + "\n"; + for (var argumentName in actionArguments) { + if (actionArguments.hasOwnProperty(argumentName)) { + if (argumentName == "type") { + continue; + } + var arg = actionArguments[argumentName]; + var argType = toType(arg); + var argString = arg; + if (argType == "object") { + if (Object.keys(arg).length == 3) { + argString = vec3toStr(arg, 1); + } + } else if (argType == "number") { + argString = arg.toFixed(2); + } + result += argumentName + ": " + // + toType(arg) + " -- " + + argString + "\n"; + } + } + + return result; +} + + +function updateOverlay(entityID, actionText) { + var properties = Entities.getEntityProperties(entityID, ["position", "dimensions"]); + var position = Vec3.sum(properties.position, {x:0, y:properties.dimensions.y, z:0}); + // print("position: " + vec3toStr(position) + " " + actionText); + if (entityID in overlays) { + var overlay = overlays[entityID]; + Overlays.editOverlay(overlay, { + text: actionText, + position: position + }); + } else { + var lines = actionText.split(/\r\n|\r|\n/); + + var maxLineLength = lines[0].length; + for (var i = 1; i < lines.length; i++) { + if (lines[i].length > maxLineLength) { + maxLineLength = lines[i].length; + } + } + + var textWidth = maxLineLength * 0.034; // XXX how to know this? + var textHeight = .5; + var numberOfLines = lines.length; + var textMargin = 0.05; + var lineHeight = (textHeight - (2 * textMargin)) / numberOfLines; + + overlays[entityID] = Overlays.addOverlay("text3d", { + position: position, + dimensions: { x: textWidth, y: textHeight }, + backgroundColor: { red: 0, green: 0, blue: 0}, + color: { red: 255, green: 255, blue: 255}, + topMargin: textMargin, + leftMargin: textMargin, + bottomMargin: textMargin, + rightMargin: textMargin, + text: actionText, + lineHeight: lineHeight, + alpha: 0.9, + backgroundAlpha: 0.9, + ignoreRayIntersection: true, + visible: true, + isFacingAvatar: true + }); + } +} + + +function cleanup() { + for (var entityID in overlays) { + Overlays.deleteOverlay(overlays[entityID]); + } +} + + +Script.setInterval(function() { + var nearbyEntities = Entities.findEntities(MyAvatar.position, INSPECT_RADIUS); + for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) { + var entityID = nearbyEntities[entityIndex]; + var actionIDs = Entities.getActionIDs(entityID); + var actionText = "" + for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { + var actionID = actionIDs[actionIndex]; + var actionArguments = Entities.getActionArguments(entityID, actionID); + var actionArgumentText = actionArgumentsToString(actionArguments); + if (actionArgumentText != "") { + actionText += "-----------------\n"; + actionText += actionArgumentText; + } + } + if (actionText != "") { + updateOverlay(entityID, actionText); + } + + // if an entity no longer has an action, remove its overlay + if (actionIDs.length == 0) { + if (entityID in overlays) { + Overlays.deleteOverlay(overlays[entityID]); + delete overlays[entityID]; + } + } + } + + + // if an entity is too far away, remove its overlay + for (var entityID in overlays) { + var position = Entities.getEntityProperties(entityID, ["position"]).position; + if (Vec3.distance(position, MyAvatar.position) > INSPECT_RADIUS) { + Overlays.deleteOverlay(overlays[entityID]); + delete overlays[entityID]; + } + } + +}, 100); + + +Script.scriptEnding.connect(cleanup); diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 388c042285..5705bd4498 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -66,6 +66,7 @@ var MSEC_PER_SEC = 1000.0; // these control how long an abandoned pointer line will hang around var startTime = Date.now(); var LIFETIME = 10; +var ACTION_LIFETIME = 10; // seconds // states for the state machine var STATE_OFF = 0; @@ -81,14 +82,25 @@ var STATE_RELEASE = 8; var GRAB_USER_DATA_KEY = "grabKey"; var GRABBABLE_DATA_KEY = "grabbableKey"; -// HACK -- until we have collision groups, don't allow held object to collide with avatar -var AVATAR_COLLISIONS_MENU_ITEM = "Enable avatar collisions"; +function getTag() { + return "grab-" + MyAvatar.sessionUUID; +} - -// HACK -- until we have collision groups, don't allow held object to collide with avatar -var initialAvatarCollisionsMenu = Menu.isOptionChecked(AVATAR_COLLISIONS_MENU_ITEM); -var currentAvatarCollisionsMenu = initialAvatarCollisionsMenu; -var noCollisionsCount = 0; // how many hands want collisions disabled? +function entityIsGrabbedByOther(entityID) { + var actionIDs = Entities.getActionIDs(entityID); + for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { + var actionID = actionIDs[actionIndex]; + var actionArguments = Entities.getActionArguments(entityID, actionID); + var tag = actionArguments["tag"]; + if (tag == getTag()) { + continue; + } + if (tag.slice(0, 5) == "grab-") { + return true; + } + } + return false; +} function MyController(hand, triggerAction) { @@ -152,28 +164,6 @@ function MyController(hand, triggerAction) { } }; - // HACK -- until we have collision groups, don't allow held object to collide with avatar - this.disableAvatarCollisions = function() { - noCollisionsCount += 1; - if (currentAvatarCollisionsMenu != false) { - currentAvatarCollisionsMenu = false; - Menu.setIsOptionChecked(AVATAR_COLLISIONS_MENU_ITEM, false); - MyAvatar.updateMotionBehaviorFromMenu(); - } - } - - // HACK -- until we have collision groups, don't allow held object to collide with avatar - this.revertAvatarCollisions = function() { - noCollisionsCount -= 1; - if (noCollisionsCount < 1) { - if (currentAvatarCollisionsMenu != initialAvatarCollisionsMenu) { - currentAvatarCollisionsMenu = initialAvatarCollisionsMenu; - Menu.setIsOptionChecked(AVATAR_COLLISIONS_MENU_ITEM, initialAvatarCollisionsMenu); - MyAvatar.updateMotionBehaviorFromMenu(); - } - } - } - this.lineOn = function(closePoint, farPoint, color) { // draw a line if (this.pointer === null) { @@ -267,6 +257,10 @@ function MyController(hand, triggerAction) { this.state = STATE_NEAR_GRABBING; } else { + if (entityIsGrabbedByOther(intersection.entityID)) { + // don't allow two people to distance grab the same object + return; + } // the hand is far from the intersected object. go into distance-holding mode this.state = STATE_DISTANCE_HOLDING; this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); @@ -306,9 +300,6 @@ function MyController(hand, triggerAction) { this.distanceHolding = function() { - // HACK -- until we have collision groups, don't allow held object to collide with avatar - this.disableAvatarCollisions(); - var handControllerPosition = Controller.getSpatialControlPosition(this.palm); var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]); @@ -320,11 +311,14 @@ function MyController(hand, triggerAction) { this.handPreviousPosition = handControllerPosition; this.handPreviousRotation = handRotation; + this.actionID = NULL_ACTION_ID; this.actionID = Entities.addAction("spring", this.grabbedEntity, { targetPosition: this.currentObjectPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + tag: getTag(), + lifetime: ACTION_LIFETIME }); if (this.actionID === NULL_ACTION_ID) { this.actionID = null; @@ -348,9 +342,6 @@ function MyController(hand, triggerAction) { this.continueDistanceHolding = function() { if (this.triggerSmoothedReleased()) { - // HACK -- until we have collision groups, don't allow held object to collide with avatar - this.revertAvatarCollisions(); - this.state = STATE_RELEASE; return; } @@ -390,11 +381,11 @@ function MyController(hand, triggerAction) { var handMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, handToAvatar), handToAvatar); var objectMovementFromTurning = Vec3.subtract(Quat.multiply(avatarDeltaOrientation, objectToAvatar), objectToAvatar); this.currentAvatarOrientation = currentOrientation; - + // how far did hand move this timestep? var handMoved = Vec3.subtract(handControllerPosition, this.handPreviousPosition); this.handPreviousPosition = handControllerPosition; - + // magnify the hand movement but not the change from avatar movement & rotation handMoved = Vec3.subtract(handMoved, avatarDeltaPosition); handMoved = Vec3.subtract(handMoved, handMovementFromTurning); @@ -424,19 +415,14 @@ function MyController(hand, triggerAction) { targetPosition: this.currentObjectPosition, linearTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, targetRotation: this.currentObjectRotation, - angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME + angularTimeScale: DISTANCE_HOLDING_ACTION_TIMEFRAME, + lifetime: ACTION_LIFETIME }); }; this.nearGrabbing = function() { - // HACK -- until we have collision groups, don't allow held object to collide with avatar - this.disableAvatarCollisions(); - if (this.triggerSmoothedReleased()) { - // HACK -- until we have collision groups, don't allow held object to collide with avatar - this.revertAvatarCollisions(); - this.state = STATE_RELEASE; return; } @@ -457,11 +443,13 @@ function MyController(hand, triggerAction) { var offset = Vec3.subtract(currentObjectPosition, handPosition); var offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, offsetRotation)), offset); + this.actionID = NULL_ACTION_ID; this.actionID = Entities.addAction("hold", this.grabbedEntity, { hand: this.hand === RIGHT_HAND ? "right" : "left", timeScale: NEAR_GRABBING_ACTION_TIMEFRAME, relativePosition: offsetPosition, - relativeRotation: offsetRotation + relativeRotation: offsetRotation, + lifetime: ACTION_LIFETIME }); if (this.actionID === NULL_ACTION_ID) { this.actionID = null; @@ -483,9 +471,6 @@ function MyController(hand, triggerAction) { this.continueNearGrabbing = function() { if (this.triggerSmoothedReleased()) { - // HACK -- until we have collision groups, don't allow held object to collide with avatar - this.revertAvatarCollisions(); - this.state = STATE_RELEASE; return; } @@ -495,7 +480,7 @@ function MyController(hand, triggerAction) { // object's actual held offset is an idea intended to make it easier to throw things: // Because we might catch something or transfer it between hands without a good idea // of it's actual offset, let's try imparting a velocity which is at a fixed radius - // from the palm. + // from the palm. var handControllerPosition = Controller.getSpatialControlPosition(this.tip); var now = Date.now(); @@ -507,6 +492,8 @@ function MyController(hand, triggerAction) { this.currentHandControllerTipPosition = handControllerPosition; this.currentObjectTime = now; Entities.callEntityMethod(this.grabbedEntity, "continueNearGrab"); + + Entities.updateAction(this.grabbedEntity, this.actionID, {lifetime: ACTION_LIFETIME}); }; this.nearGrabbingNonColliding = function() { diff --git a/examples/grab.js b/examples/grab.js index 05bcf128e2..43a5ed220b 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -13,7 +13,27 @@ var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed var ZERO_VEC3 = {x: 0, y: 0, z: 0}; var IDENTITY_QUAT = {x: 0, y: 0, z: 0, w: 0}; +var ACTION_LIFETIME = 120; // 2 minutes +function getTag() { + return "grab-" + MyAvatar.sessionUUID; +} + +function entityIsGrabbedByOther(entityID) { + var actionIDs = Entities.getActionIDs(entityID); + for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { + var actionID = actionIDs[actionIndex]; + var actionArguments = Entities.getActionArguments(entityID, actionID); + var tag = actionArguments["tag"]; + if (tag == getTag()) { + continue; + } + if (tag.slice(0, 5) == "grab-") { + return true; + } + } + return false; +} // helper function function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event, maxDistance) { @@ -288,7 +308,7 @@ Grabber.prototype.moveEvent = function(event) { } this.currentPosition = entityProperties.position; - var actionArgs = {}; + var actionArgs = {tag: getTag(), lifetime: ACTION_LIFETIME}; if (this.mode === "rotate") { var drag = mouse.getDrag(); @@ -303,7 +323,7 @@ Grabber.prototype.moveEvent = function(event) { // var qZero = entityProperties.rotation; //var qZero = this.lastRotation; this.lastRotation = Quat.multiply(deltaQ, this.lastRotation); - actionArgs = {targetRotation: this.lastRotation, angularTimeScale: 0.1}; + actionArgs = {targetRotation: this.lastRotation, angularTimeScale: 0.1, tag: getTag(), lifetime: ACTION_LIFETIME}; } else { var newPointOnPlane; if (this.mode === "verticalCylinder") { @@ -327,13 +347,15 @@ Grabber.prototype.moveEvent = function(event) { } } this.targetPosition = Vec3.subtract(newPointOnPlane, this.offset); - actionArgs = {targetPosition: this.targetPosition, linearTimeScale: 0.1}; + actionArgs = {targetPosition: this.targetPosition, linearTimeScale: 0.1, tag: getTag(), lifetime: ACTION_LIFETIME}; beacon.updatePosition(this.targetPosition); } if (!this.actionID) { - this.actionID = Entities.addAction("spring", this.entityID, actionArgs); + if (!entityIsGrabbedByOther(this.entityID)) { + this.actionID = Entities.addAction("spring", this.entityID, actionArgs); + } } else { Entities.updateAction(this.entityID, this.actionID, actionArgs); } diff --git a/examples/toys/basketball_hoop/createHoop.js b/examples/toys/basketball_hoop/createHoop.js new file mode 100644 index 0000000000..3887e0b421 --- /dev/null +++ b/examples/toys/basketball_hoop/createHoop.js @@ -0,0 +1,43 @@ +// +// createHoop.js +// examples/entityScripts +// +// Created by James B. Pollack on 9/29/2015 +// Copyright 2015 High Fidelity, Inc. +// +// This is a script that creates a persistent basketball hoop with a working collision hull. Feel free to move it. +// Run basketball.js to make a basketball. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +/*global MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */ + +var hoopURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop.fbx"; +var hoopCollisionHullURL = "http://hifi-public.s3.amazonaws.com/models/basketball_hoop/basketball_hoop_collision_hull.obj"; + +var hoopStartPosition = + Vec3.sum(MyAvatar.position, + Vec3.multiplyQbyV(MyAvatar.orientation, { + x: 0, + y: 0.0, + z: -2 + })); + +var hoop = Entities.addEntity({ + type: "Model", + modelURL: hoopURL, + position: hoopStartPosition, + shapeType: 'compound', + gravity: { + x: 0, + y: -9.8, + z: 0 + }, + dimensions: { + x: 1.89, + y: 3.99, + z: 3.79 + }, + compoundShapeURL: hoopCollisionHullURL +}); + diff --git a/examples/utilities/tools/currentAPI.js b/examples/utilities/tools/currentAPI.js index 30b24910f9..cb9f152794 100644 --- a/examples/utilities/tools/currentAPI.js +++ b/examples/utilities/tools/currentAPI.js @@ -10,22 +10,21 @@ // var array = []; -var buffer = "\n\n\n\n\n======= JS API list ======="; function listKeys(string, object) { - if (string == "listKeys" || string == "array" || string == "buffer" || string == "i") { + if (string === "listKeys" || string === "array" || string === "buffer" || string === "i") { return; } - if (typeof(object) != "object") { + if (typeof(object) !== "object" || object === null) { array.push(string + " " + typeof(object)); return; } var keys = Object.keys(object); for (var i = 0; i < keys.length; ++i) { - if (string == "") { + if (string === "") { listKeys(keys[i], object[keys[i]]); - } else { + } else if (keys[i] !== "parent") { listKeys(string + "." + keys[i], object[keys[i]]); } } @@ -34,9 +33,10 @@ function listKeys(string, object) { listKeys("", this); array.sort(); +var buffer = "\n======= JS API list ======="; for (var i = 0; i < array.length; ++i) { - buffer = buffer + "\n" + array[i]; + buffer += "\n" + array[i]; } -buffer = buffer + "\n========= API END =========\n\n\n\n\n"; +buffer += "\n========= API END =========\n"; print(buffer); diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 72eefaf7e8..dceecddfe0 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -26,13 +26,14 @@ { "jointName": "Neck", "positionVar": "neckPosition", - "rotationVar": "neckRotation" + "rotationVar": "neckRotation", + "typeVar": "headAndNeckType" }, { "jointName": "Head", "positionVar": "headPosition", "rotationVar": "headRotation", - "typeVar": "headType" + "typeVar": "headAndNeckType" } ] }, diff --git a/interface/resources/qml/VrMenu.qml b/interface/resources/qml/VrMenu.qml index ef7ae852d4..14a4a449fd 100644 --- a/interface/resources/qml/VrMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -196,7 +196,6 @@ Hifi.VrMenu { function insertItem(menu, beforeItem, newMenuItem) { for (var i = 0; i < menu.items.length; ++i) { - console.log(menu.items[i]); if (menu.items[i] === beforeItem) { return menu.insertItem(i, newMenuItem); } diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp index dca1015ecc..2879c19eaa 100644 --- a/interface/src/InterfaceActionFactory.cpp +++ b/interface/src/InterfaceActionFactory.cpp @@ -43,6 +43,9 @@ EntityActionPointer InterfaceActionFactory::factory(EntityActionType type, if (action) { bool ok = action->updateArguments(arguments); if (ok) { + if (action->lifetimeIsOver()) { + return nullptr; + } return action; } } @@ -63,5 +66,9 @@ EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEnt if (action) { action->deserialize(data); } + if (action->lifetimeIsOver()) { + return nullptr; + } + return action; } diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 4ecdb692ac..1fa50b79fd 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -84,6 +84,9 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { bool AvatarActionHold::updateArguments(QVariantMap arguments) { + if (!ObjectAction::updateArguments(arguments)) { + return false; + } bool ok = true; glm::vec3 relativePosition = EntityActionInterface::extractVec3Argument("hold", arguments, "relativePosition", ok, false); @@ -134,7 +137,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { QVariantMap AvatarActionHold::getArguments() { - QVariantMap arguments; + QVariantMap arguments = ObjectAction::getArguments(); withReadLock([&]{ if (!_mine) { arguments = ObjectActionSpring::getArguments(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8f7af63a81..a57740a691 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -271,6 +271,25 @@ glm::mat4 MyAvatar::getSensorToWorldMatrix() const { return _sensorToWorldMatrix; } +// returns true if pos is OUTSIDE of the vertical capsule +// where the middle cylinder length is defined by capsuleLen and the radius by capsuleRad. +static bool capsuleCheck(const glm::vec3& pos, float capsuleLen, float capsuleRad) { + const float halfCapsuleLen = capsuleLen / 2.0f; + if (fabs(pos.y) <= halfCapsuleLen) { + // cylinder check for middle capsule + glm::vec2 horizPos(pos.x, pos.z); + return glm::length(horizPos) > capsuleRad; + } else if (pos.y > halfCapsuleLen) { + glm::vec3 center(0.0f, halfCapsuleLen, 0.0f); + return glm::length(center - pos) > capsuleRad; + } else if (pos.y < halfCapsuleLen) { + glm::vec3 center(0.0f, -halfCapsuleLen, 0.0f); + return glm::length(center - pos) > capsuleRad; + } else { + return false; + } +} + // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. @@ -289,11 +308,14 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); const float STRAIGHTING_LEAN_DURATION = 0.5f; // seconds - const float STRAIGHTING_LEAN_THRESHOLD = 0.2f; // meters + + // define a vertical capsule + const float STRAIGHTING_LEAN_CAPSULE_RADIUS = 0.2f; // meters + const float STRAIGHTING_LEAN_CAPSULE_LENGTH = 0.05f; // length of the cylinder part of the capsule in meters. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); glm::vec3 diff = extractTranslation(newBodySensorMatrix) - extractTranslation(_bodySensorMatrix); - if (!_straightingLean && glm::length(diff) > STRAIGHTING_LEAN_THRESHOLD) { + if (!_straightingLean && capsuleCheck(diff, STRAIGHTING_LEAN_CAPSULE_LENGTH, STRAIGHTING_LEAN_CAPSULE_RADIUS)) { // begin homing toward derived body position. _straightingLean = true; @@ -1854,13 +1876,39 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { const glm::quat hmdOrientation = getHMDSensorOrientation(); const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation); - // In sensor space, figure out where the avatar body should be, - // by applying offsets from the avatar's neck & head joints. - vec3 localEyes = _skeletonModel.getDefaultEyeModelPosition(); - vec3 localNeck(0.0f, 0.48f, 0.0f); // start with some kind of guess if the skeletonModel is not loaded yet. - _skeletonModel.getLocalNeckPosition(localNeck); + const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f); + const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f); + const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f); + const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.0f, 0.0f); + + vec3 localEyes, localNeck; + if (!_debugDrawSkeleton) { + const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); + localEyes = rotY180 * (((DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) / 2.0f) - DEFAULT_HIPS_POS); + localNeck = rotY180 * (DEFAULT_NECK_POS - DEFAULT_HIPS_POS); + } else { + // TODO: At the moment MyAvatar does not have access to the rig, which has the skeleton, which has the bind poses. + // for now use the _debugDrawSkeleton, which is initialized with the same FBX model as the rig. + + // TODO: cache these indices. + int rightEyeIndex = _debugDrawSkeleton->nameToJointIndex("RightEye"); + int leftEyeIndex = _debugDrawSkeleton->nameToJointIndex("LeftEye"); + int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck"); + int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips"); + + glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; + glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; + glm::vec3 absNeckPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS; + glm::vec3 absHipsPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(hipsIndex).trans : DEFAULT_HIPS_POS; + + const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); + localEyes = rotY180 * (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos); + localNeck = rotY180 * (absNeckPos - absHipsPos); + } // apply simplistic head/neck model + // figure out where the avatar body should be by applying offsets from the avatar's neck & head joints. + // eyeToNeck offset is relative full HMD orientation. // while neckToRoot offset is only relative to HMDs yaw. glm::vec3 eyeToNeck = hmdOrientation * (localNeck - localEyes); diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index de226092f1..c0adba0ad6 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -89,7 +89,7 @@ static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int inde return rootIndex; } -void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets) { +void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses) { // build a list of valid targets from _targetVarVec and animVars _maxTargetIndex = -1; bool removeUnfoundJoints = false; @@ -107,7 +107,7 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: } } else { IKTarget target; - AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses); + AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); target.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans); target.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot); target.setType(animVars.lookup(targetVar.typeVar, QString(""))); @@ -154,7 +154,6 @@ void AnimInverseKinematics::solveWithCyclicCoordinateDescent(const std::vector& targets); + void computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses); void solveWithCyclicCoordinateDescent(const std::vector& targets); virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton); diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 700a8b4121..cb886cd369 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -184,6 +184,8 @@ public: case AnimVariant::Type::String: qCDebug(animation) << " " << pair.first << "=" << pair.second.getString(); break; + default: + assert("AnimVariant::Type" == "valid"); } } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0022749d51..5dc81fecfe 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1026,12 +1026,21 @@ static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const A const glm::quat hmdOrientation = hmdPose.rot * rotY180; // rotY180 will make z forward not -z // TODO: cache jointIndices + int rightEyeIndex = skeleton->nameToJointIndex("RightEye"); + int leftEyeIndex = skeleton->nameToJointIndex("LeftEye"); + int headIndex = skeleton->nameToJointIndex("Head"); + int neckIndex = skeleton->nameToJointIndex("Neck"); + + const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f); + const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f); + const glm::vec3 DEFAULT_HEAD_POS(0.0f, 1.55f, 0.0f); + const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f); // Use absolute bindPose positions just in case the relBindPose have rotations we don't expect. - glm::vec3 absRightEyePos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("RightEye")).trans; - glm::vec3 absLeftEyePos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("LeftEye")).trans; - glm::vec3 absHeadPos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Head")).trans; - glm::vec3 absNeckPos = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Neck")).trans; + glm::vec3 absRightEyePos = rightEyeIndex != -1 ? skeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; + glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? skeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; + glm::vec3 absHeadPos = headIndex != -1 ? skeleton->getAbsoluteBindPose(headIndex).trans : DEFAULT_HEAD_POS; + glm::vec3 absNeckPos = neckIndex != -1 ? skeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS; glm::vec3 absCenterEyePos = (absRightEyePos + absLeftEyePos) / 2.0f; glm::vec3 eyeOffset = absCenterEyePos - absHeadPos; @@ -1079,7 +1088,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.set("headPosition", headPos); _animVars.set("headRotation", headRot); - _animVars.set("headType", QString("RotationAndPosition")); + _animVars.set("headAndNeckType", QString("RotationAndPosition")); _animVars.set("neckPosition", neckPos); _animVars.set("neckRotation", neckRot); @@ -1092,7 +1101,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.unset("headPosition"); _animVars.set("headRotation", realLocalHeadOrientation); - _animVars.set("headType", QString("RotationOnly")); + _animVars.set("headAndNeckType", QString("RotationOnly")); _animVars.unset("neckPosition"); _animVars.unset("neckRotation"); } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 3bd147d398..f64504d662 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -966,7 +966,8 @@ bool AvatarData::hasIdentityChangedAfterParsing(NLPacket& packet) { QByteArray AvatarData::identityByteArray() { QByteArray identityData; QDataStream identityStream(&identityData, QIODevice::Append); - const QUrl& urlToSend = (_skeletonModelURL == AvatarData::defaultFullAvatarModelUrl()) ? QUrl("") : _skeletonModelURL; + QUrl emptyURL(""); + const QUrl& urlToSend = (_skeletonModelURL == AvatarData::defaultFullAvatarModelUrl()) ? emptyURL : _skeletonModelURL; identityStream << QUuid() << _faceModelURL << urlToSend << _attachmentData << _displayName; diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index a4f1c8ea15..e61019c98c 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -46,6 +46,8 @@ public: static EntityActionType actionTypeFromString(QString actionTypeString); static QString actionTypeToString(EntityActionType actionType); + virtual bool lifetimeIsOver() { return false; } + protected: virtual glm::vec3 getPosition() = 0; virtual void setPosition(glm::vec3 position) = 0; diff --git a/libraries/networking/src/NLPacketList.cpp b/libraries/networking/src/NLPacketList.cpp index 3b115c558b..318fb037a1 100644 --- a/libraries/networking/src/NLPacketList.cpp +++ b/libraries/networking/src/NLPacketList.cpp @@ -23,7 +23,8 @@ std::unique_ptr NLPacketList::create(PacketType packetType, QByteA } std::unique_ptr NLPacketList::fromPacketList(std::unique_ptr packetList) { - auto nlPacketList = std::unique_ptr(new NLPacketList(std::move(*packetList.release()))); nlPacketList->open(ReadOnly); + auto nlPacketList = std::unique_ptr(new NLPacketList(std::move(*packetList.release()))); + nlPacketList->open(ReadOnly); return nlPacketList; } diff --git a/libraries/networking/src/SentPacketHistory.cpp b/libraries/networking/src/SentPacketHistory.cpp index c6eec8eb63..fbb7eff41a 100644 --- a/libraries/networking/src/SentPacketHistory.cpp +++ b/libraries/networking/src/SentPacketHistory.cpp @@ -35,6 +35,7 @@ void SentPacketHistory::packetSent(uint16_t sequenceNumber, const NLPacket& pack } _newestSequenceNumber = sequenceNumber; + QWriteLocker locker(&_packetsLock); _sentPackets.insert(NLPacket::createCopy(packet)); } @@ -48,6 +49,11 @@ const NLPacket* SentPacketHistory::getPacket(uint16_t sequenceNumber) const { if (seqDiff < 0) { seqDiff += UINT16_RANGE; } - - return _sentPackets.get(seqDiff)->get(); + + QReadLocker locker(&_packetsLock); + auto packet = _sentPackets.get(seqDiff); + if (packet) { + return packet->get(); + } + return nullptr; } diff --git a/libraries/networking/src/SentPacketHistory.h b/libraries/networking/src/SentPacketHistory.h index 1808e0020b..72150658e3 100644 --- a/libraries/networking/src/SentPacketHistory.h +++ b/libraries/networking/src/SentPacketHistory.h @@ -12,7 +12,9 @@ #define hifi_SentPacketHistory_h #include -#include + +#include +#include #include "NLPacket.h" #include "RingBufferHistory.h" @@ -29,6 +31,7 @@ public: const NLPacket* getPacket(uint16_t sequenceNumber) const; private: + mutable QReadWriteLock _packetsLock { QReadWriteLock::Recursive }; RingBufferHistory> _sentPackets; // circular buffer uint16_t _newestSequenceNumber; diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 5f0db9412c..0422c03297 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -49,7 +49,8 @@ void ThreadedAssignment::setFinished(bool isFinished) { } if (_statsTimer) { - _statsTimer->stop(); + _statsTimer->deleteLater(); + _statsTimer = nullptr; } // call our virtual aboutToFinish method - this gives the ThreadedAssignment subclass a chance to cleanup @@ -105,7 +106,7 @@ void ThreadedAssignment::sendStatsPacket() { void ThreadedAssignment::startSendingStats() { // send the stats packet every 1s if (!_statsTimer) { - _statsTimer = new QTimer(); + _statsTimer = new QTimer; connect(_statsTimer, &QTimer::timeout, this, &ThreadedAssignment::sendStatsPacket); } diff --git a/libraries/networking/src/udt/CongestionControl.cpp b/libraries/networking/src/udt/CongestionControl.cpp index ea46d60acb..c1feae3911 100644 --- a/libraries/networking/src/udt/CongestionControl.cpp +++ b/libraries/networking/src/udt/CongestionControl.cpp @@ -161,13 +161,16 @@ void DefaultCC::onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) { _lastDecreaseMaxSeq = _sendCurrSeqNum; - // avoid synchronous rate decrease across connections using randomization - std::random_device rd; - std::mt19937 generator(rd()); - std::uniform_int_distribution<> distribution(1, _avgNAKNum); - - _randomDecreaseThreshold = distribution(generator); - + if (_avgNAKNum < 1) { + _randomDecreaseThreshold = 1; + } else { + // avoid synchronous rate decrease across connections using randomization + std::random_device rd; + std::mt19937 generator(rd()); + std::uniform_int_distribution<> distribution(1, std::max(1, _avgNAKNum)); + + _randomDecreaseThreshold = distribution(generator); + } } else if ((_decreaseCount++ < MAX_DECREASES_PER_CONGESTION_EPOCH) && ((++_nakCount % _randomDecreaseThreshold) == 0)) { // there have been fewer than MAX_DECREASES_PER_CONGESTION_EPOCH AND this NAK matches the random count at which we // decided we would decrease the packet send period diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 1bda840a6c..e8b22f6ab8 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -32,7 +32,7 @@ Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::uniq _destination(destination), _congestionControl(move(congestionControl)) { - Q_ASSERT_X(socket, "Connection::Connection", "Must be called with a valid Socket*"); + Q_ASSERT_X(parentSocket, "Connection::Connection", "Must be called with a valid Socket*"); Q_ASSERT_X(_congestionControl, "Connection::Connection", "Must be called with a valid CongestionControl object"); _congestionControl->init(); diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index ae783dabe3..7978e77ad7 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -28,7 +28,8 @@ class Packet; class PacketList : public QIODevice { Q_OBJECT public: - static std::unique_ptr create(PacketType packetType, QByteArray extendedHeader = QByteArray(), bool isReliable = false, bool isOrdered = false); + static std::unique_ptr create(PacketType packetType, QByteArray extendedHeader = QByteArray(), + bool isReliable = false, bool isOrdered = false); static std::unique_ptr fromReceivedPackets(std::list>&& packets); bool isReliable() const { return _isReliable; } diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 1be271cbdd..495effc825 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -346,6 +346,7 @@ void OctreeEditPacketSender::processNackPacket(NLPacket& packet, SharedNodePoint if (_sentPacketHistories.count(sendingNode->getUUID()) == 0) { return; } + const SentPacketHistory& sentPacketHistory = _sentPacketHistories[sendingNode->getUUID()]; // read sequence numbers and queue packets for resend diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 5205e08c62..82395c21cf 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -30,6 +30,18 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta dynamicsWorld->removeAction(this); return; } + + if (_expires > 0) { + quint64 now = usecTimestampNow(); + if (now > _expires) { + EntityItemPointer ownerEntity = _ownerEntity.lock(); + _active = false; + if (ownerEntity) { + ownerEntity->removeAction(nullptr, getID()); + } + } + } + if (!_active) { return; } @@ -37,6 +49,40 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta updateActionWorker(deltaTimeStep); } +bool ObjectAction::updateArguments(QVariantMap arguments) { + bool lifetimeSet = true; + float lifetime = EntityActionInterface::extractFloatArgument("action", arguments, "lifetime", lifetimeSet, false); + if (lifetimeSet) { + quint64 now = usecTimestampNow(); + _expires = now + (quint64)(lifetime * USECS_PER_SECOND); + } else { + _expires = 0; + } + + bool tagSet = true; + QString tag = EntityActionInterface::extractStringArgument("action", arguments, "tag", tagSet, false); + if (tagSet) { + _tag = tag; + } else { + tag = ""; + } + + return true; +} + +QVariantMap ObjectAction::getArguments() { + QVariantMap arguments; + if (_expires == 0) { + arguments["lifetime"] = 0.0f; + } else { + quint64 now = usecTimestampNow(); + arguments["lifetime"] = (float)(_expires - now) / (float)USECS_PER_SECOND; + } + arguments["tag"] = _tag; + return arguments; +} + + void ObjectAction::debugDraw(btIDebugDraw* debugDrawer) { } @@ -136,3 +182,14 @@ void ObjectAction::activateBody() { } } +bool ObjectAction::lifetimeIsOver() { + if (_expires == 0) { + return false; + } + + quint64 now = usecTimestampNow(); + if (now >= _expires) { + return true; + } + return false; +} diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 4d91d0dbb1..5c29ac9892 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -33,8 +33,8 @@ public: virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; } virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; } - virtual bool updateArguments(QVariantMap arguments) = 0; - virtual QVariantMap getArguments() = 0; + virtual bool updateArguments(QVariantMap arguments); + virtual QVariantMap getArguments(); // this is called from updateAction and should be overridden by subclasses virtual void updateActionWorker(float deltaTimeStep) = 0; @@ -46,6 +46,8 @@ public: virtual QByteArray serialize() const = 0; virtual void deserialize(QByteArray serializedArguments) = 0; + virtual bool lifetimeIsOver(); + protected: virtual btRigidBody* getRigidBody(); @@ -59,11 +61,11 @@ protected: virtual void setAngularVelocity(glm::vec3 angularVelocity); virtual void activateBody(); -private: - -protected: bool _active; EntityItemWeakPointer _ownerEntity; + + quint64 _expires; // in seconds since epoch + QString _tag; }; #endif // hifi_ObjectAction_h diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index 2c1b391ba5..448ec34689 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -80,6 +80,9 @@ void ObjectActionOffset::updateActionWorker(btScalar deltaTimeStep) { bool ObjectActionOffset::updateArguments(QVariantMap arguments) { + if (!ObjectAction::updateArguments(arguments)) { + return false; + } bool ok = true; glm::vec3 pointToOffsetFrom = EntityActionInterface::extractVec3Argument("offset action", arguments, "pointToOffsetFrom", ok, true); @@ -90,7 +93,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) { ok = true; float linearTimeScale = EntityActionInterface::extractFloatArgument("offset action", arguments, "linearTimeScale", ok, false); - if (!ok) { + if (!ok) { linearTimeScale = _linearTimeScale; } @@ -119,7 +122,7 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) { } QVariantMap ObjectActionOffset::getArguments() { - QVariantMap arguments; + QVariantMap arguments = ObjectAction::getArguments(); withReadLock([&] { arguments["pointToOffsetFrom"] = glmToQMap(_pointToOffsetFrom); arguments["linearTimeScale"] = _linearTimeScale; @@ -140,6 +143,9 @@ QByteArray ObjectActionOffset::serialize() const { dataStream << _linearTimeScale; dataStream << _positionalTargetSet; + dataStream << _expires; + dataStream << _tag; + return ba; } @@ -165,5 +171,8 @@ void ObjectActionOffset::deserialize(QByteArray serializedArguments) { dataStream >> _linearTimeScale; dataStream >> _positionalTargetSet; + dataStream >> _expires; + dataStream >> _tag; + _active = true; } diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index d163933299..7d0bab5143 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -109,6 +109,9 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { const float MIN_TIMESCALE = 0.1f; bool ObjectActionSpring::updateArguments(QVariantMap arguments) { + if (!ObjectAction::updateArguments(arguments)) { + return false; + } // targets are required, spring-constants are optional bool ok = true; glm::vec3 positionalTarget = @@ -155,7 +158,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) { } QVariantMap ObjectActionSpring::getArguments() { - QVariantMap arguments; + QVariantMap arguments = ObjectAction::getArguments(); withReadLock([&] { arguments["linearTimeScale"] = _linearTimeScale; arguments["targetPosition"] = glmToQMap(_positionalTarget); @@ -182,6 +185,9 @@ QByteArray ObjectActionSpring::serialize() const { dataStream << _angularTimeScale; dataStream << _rotationalTargetSet; + dataStream << _expires; + dataStream << _tag; + return serializedActionArguments; } @@ -210,5 +216,8 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) { dataStream >> _angularTimeScale; dataStream >> _rotationalTargetSet; + dataStream >> _expires; + dataStream >> _tag; + _active = true; }