diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 44aa1146d4..1e77125d00 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -34,18 +34,18 @@ var NO_INTERSECT_COLOR = { red: 10, green: 10, blue: 255}; // line color when pi var INTERSECT_COLOR = { red: 250, green: 10, blue: 10}; // line color when pick hits var LINE_ENTITY_DIMENSIONS = { x: 1000, y: 1000,z: 1000}; var LINE_LENGTH = 500; - +var PICK_MAX_DISTANCE = 500; // max length of pick-ray ///////////////////////////////////////////////////////////////// // // near grabbing // -var GRAB_RADIUS = 0.3; // if the ray misses but an object is this close, it will still be selected var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var NEAR_GRABBING_VELOCITY_SMOOTH_RATIO = 1.0; // adjust time-averaging of held object's velocity. 1.0 to disable. -var NEAR_PICK_MAX_DISTANCE = 0.6; // max length of pick-ray for close grabbing to be selected +var NEAR_PICK_MAX_DISTANCE = 0.2; // max length of pick-ray for close grabbing to be selected var RELEASE_VELOCITY_MULTIPLIER = 1.5; // affects throwing things +var PICK_BACKOFF_DISTANCE = 0.1; // helps when hand is intersecting the grabble object ///////////////////////////////////////////////////////////////// // @@ -256,12 +256,18 @@ function MyController(hand, triggerAction) { // the trigger is being pressed, do a ray test var handPosition = this.getHandPosition(); - var pickRay = { + var distantPickRay = { origin: handPosition, - direction: Quat.getUp(this.getHandRotation()) + direction: Quat.getUp(this.getHandRotation()), + length: PICK_MAX_DISTANCE + }; + var palmPickRay = { + origin: handPosition, + direction: Quat.getFront(this.getHandRotation()), + length: NEAR_PICK_MAX_DISTANCE }; - this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); + this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); // don't pick 60x per second. do this check after updating the line so it's not jumpy. var now = Date.now(); @@ -270,67 +276,54 @@ function MyController(hand, triggerAction) { } this.lastPickTime = now; - var intersection = Entities.findRayIntersection(pickRay, true); - if (intersection.intersects && intersection.properties.locked === 0) { - // the ray is intersecting something we can move. - var handControllerPosition = Controller.getSpatialControlPosition(this.palm); - var intersectionDistance = Vec3.distance(handControllerPosition, intersection.intersection); - this.grabbedEntity = intersection.entityID; + var pickRays = [distantPickRay, palmPickRay]; + for (var index=0; index < pickRays.length; ++index) { + var pickRay = pickRays[index]; + var directionNormalized = Vec3.normalize(pickRay.direction); + var directionBacked = Vec3.multiply(directionNormalized, PICK_BACKOFF_DISTANCE); + var pickRayBacked = { + origin: Vec3.subtract(pickRay.origin, directionBacked), + direction: pickRay.direction + }; - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, DEFAULT_GRABBABLE_DATA); - if (grabbableData.grabbable === false) { - this.grabbedEntity = null; - return; - } - if (intersectionDistance < NEAR_PICK_MAX_DISTANCE) { - // the hand is very close to the intersected object. go into close-grabbing mode. - if (intersection.properties.collisionsWillMove === 1) { - this.setState(STATE_NEAR_GRABBING); - } else { - this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); - } - } else { - // don't allow two people to distance grab the same object - if (entityIsGrabbedByOther(intersection.entityID)) { + var intersection = Entities.findRayIntersection(pickRayBacked, true); + if (intersection.intersects && intersection.properties.locked === 0) { + // the ray is intersecting something we can move. + var intersectionDistance = Vec3.distance(pickRay.origin, intersection.intersection); + this.grabbedEntity = intersection.entityID; + + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, + intersection.entityID, + DEFAULT_GRABBABLE_DATA); + if (grabbableData.grabbable === false) { this.grabbedEntity = null; - } else { - // the hand is far from the intersected object. go into distance-holding mode + continue; + } + if (intersectionDistance > pickRay.length) { + // too far away for this ray. + continue; + } + if (intersectionDistance <= NEAR_PICK_MAX_DISTANCE) { + // the hand is very close to the intersected object. go into close-grabbing mode. if (intersection.properties.collisionsWillMove === 1) { - this.setState(STATE_DISTANCE_HOLDING); + this.setState(STATE_NEAR_GRABBING); } else { - this.setState(STATE_FAR_GRABBING_NON_COLLIDING); + this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); + } + } else { + // don't allow two people to distance grab the same object + if (entityIsGrabbedByOther(intersection.entityID)) { + this.grabbedEntity = null; + } else { + // the hand is far from the intersected object. go into distance-holding mode + if (intersection.properties.collisionsWillMove === 1) { + this.setState(STATE_DISTANCE_HOLDING); + } else { + this.setState(STATE_FAR_GRABBING_NON_COLLIDING); + } } } } - } else { - // forward ray test failed, try sphere test. - var nearbyEntities = Entities.findEntities(handPosition, GRAB_RADIUS); - var minDistance = GRAB_RADIUS; - var i, props, distance; - - for (i = 0; i < nearbyEntities.length; i++) { - - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, nearbyEntities[i], DEFAULT_GRABBABLE_DATA); - if (grabbableData.grabbable === false) { - return; - } - - props = Entities.getEntityProperties(nearbyEntities[i], ["position", "name", "collisionsWillMove", "locked"]); - - distance = Vec3.distance(props.position, handPosition); - if (distance < minDistance && props.name !== "pointer") { - this.grabbedEntity = nearbyEntities[i]; - minDistance = distance; - } - } - if (this.grabbedEntity === null) { - // this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); - } else if (props.locked === 0 && props.collisionsWillMove === 1) { - this.setState(STATE_NEAR_GRABBING); - } else if (props.collisionsWillMove === 0) { - // We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event - this.setState(STATE_NEAR_GRABBING_NON_COLLIDING); - } } }; @@ -393,7 +386,8 @@ function MyController(hand, triggerAction) { this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); // the action was set up on a previous call. update the targets. - var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR); + var radius = Math.max(Vec3.distance(this.currentObjectPosition, handControllerPosition) * + DISTANCE_HOLDING_RADIUS_FACTOR, DISTANCE_HOLDING_RADIUS_FACTOR); // how far did avatar move this timestep? var currentPosition = MyAvatar.position; @@ -479,12 +473,23 @@ function MyController(hand, triggerAction) { var handRotation = this.getHandRotation(); var handPosition = this.getHandPosition(); - var objectRotation = grabbedProperties.rotation; - this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); - var currentObjectPosition = grabbedProperties.position; - var offset = Vec3.subtract(currentObjectPosition, handPosition); - this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + if (grabbableData.spatialKey) { + if (grabbableData.spatialKey.relativePosition) { + this.offsetPosition = grabbableData.spatialKey.relativePosition; + } + if (grabbableData.spatialKey.relativeRotation) { + this.offsetRotation = grabbableData.spatialKey.relativeRotation; + } + } else { + var objectRotation = grabbedProperties.rotation; + this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); + + var currentObjectPosition = grabbedProperties.position; + var offset = Vec3.subtract(currentObjectPosition, handPosition); + this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset); + } this.actionID = NULL_ACTION_ID; this.actionID = Entities.addAction("hold", this.grabbedEntity, { @@ -584,7 +589,7 @@ function MyController(hand, triggerAction) { this.setState(STATE_RELEASE); return; } - + Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); }; @@ -600,6 +605,16 @@ function MyController(hand, triggerAction) { direction: Quat.getUp(this.getHandRotation()) }; + var now = Date.now(); + if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) { + var intersection = Entities.findRayIntersection(pickRay, true); + this.lastPickTime = now; + if (intersection.entityID != this.grabbedEntity) { + this.setState(STATE_RELEASE); + return; + } + } + this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); Entities.callEntityMethod(this.grabbedEntity, "continueFarGrabbingNonColliding"); }; diff --git a/interface/src/InterfaceActionFactory.cpp b/interface/src/InterfaceActionFactory.cpp index e2cbdac425..f814df9a99 100644 --- a/interface/src/InterfaceActionFactory.cpp +++ b/interface/src/InterfaceActionFactory.cpp @@ -12,7 +12,6 @@ #include -#include #include #include @@ -29,8 +28,6 @@ EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& i return (EntityActionPointer) new ObjectActionSpring(id, ownerEntity); case ACTION_TYPE_HOLD: return (EntityActionPointer) new AvatarActionHold(id, ownerEntity); - case ACTION_TYPE_KINEMATIC_HOLD: - return (EntityActionPointer) new AvatarActionKinematicHold(id, ownerEntity); } assert(false); @@ -69,6 +66,7 @@ EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEnt if (action) { action->deserialize(data); if (action->lifetimeIsOver()) { + qDebug() << "InterfaceActionFactory::factoryBA lifetimeIsOver during action creation"; return nullptr; } } diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 3c57db084b..cf455ea98c 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -21,7 +21,8 @@ AvatarActionHold::AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntit ObjectActionSpring(id, ownerEntity), _relativePosition(glm::vec3(0.0f)), _relativeRotation(glm::quat()), - _hand("right") + _hand("right"), + _holderID(QUuid()) { _type = ACTION_TYPE_HOLD; #if WANT_DEBUG @@ -39,42 +40,92 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) { bool gotLock = false; glm::quat rotation; glm::vec3 position; - glm::vec3 offset; + std::shared_ptr holdingAvatar = nullptr; gotLock = withTryReadLock([&]{ - auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 palmPosition; - glm::quat palmRotation; - if (_hand == "right") { - palmPosition = myAvatar->getRightPalmPosition(); - palmRotation = myAvatar->getRightPalmRotation(); - } else { - palmPosition = myAvatar->getLeftPalmPosition(); - palmRotation = myAvatar->getLeftPalmRotation(); - } + QSharedPointer avatarManager = DependencyManager::get(); + AvatarSharedPointer holdingAvatarData = avatarManager->getAvatarBySessionID(_holderID); + holdingAvatar = std::static_pointer_cast(holdingAvatarData); - rotation = palmRotation * _relativeRotation; - offset = rotation * _relativePosition; - position = palmPosition + offset; + if (holdingAvatar) { + glm::vec3 offset; + glm::vec3 palmPosition; + glm::quat palmRotation; + if (_hand == "right") { + palmPosition = holdingAvatar->getRightPalmPosition(); + palmRotation = holdingAvatar->getRightPalmRotation(); + } else { + palmPosition = holdingAvatar->getLeftPalmPosition(); + palmRotation = holdingAvatar->getLeftPalmRotation(); + } + + rotation = palmRotation * _relativeRotation; + offset = rotation * _relativePosition; + position = palmPosition + offset; + } }); - if (gotLock) { - gotLock = withTryWriteLock([&]{ - _positionalTarget = position; - _rotationalTarget = rotation; - _positionalTargetSet = true; - _rotationalTargetSet = true; - auto ownerEntity = _ownerEntity.lock(); - if (ownerEntity) { - ownerEntity->setActionDataDirty(true); + if (holdingAvatar) { + if (gotLock) { + gotLock = withTryWriteLock([&]{ + _positionalTarget = position; + _rotationalTarget = rotation; + _positionalTargetSet = true; + _rotationalTargetSet = true; + _active = true; + }); + if (gotLock) { + if (_kinematic) { + doKinematicUpdate(deltaTimeStep); + } else { + activateBody(); + ObjectActionSpring::updateActionWorker(deltaTimeStep); + } } - }); - } - if (gotLock) { - ObjectActionSpring::updateActionWorker(deltaTimeStep); + } } } +void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) { + auto ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + qDebug() << "AvatarActionHold::doKinematicUpdate -- no owning entity"; + return; + } + void* physicsInfo = ownerEntity->getPhysicsInfo(); + if (!physicsInfo) { + qDebug() << "AvatarActionHold::doKinematicUpdate -- no owning physics info"; + return; + } + ObjectMotionState* motionState = static_cast(physicsInfo); + btRigidBody* rigidBody = motionState ? motionState->getRigidBody() : nullptr; + if (!rigidBody) { + qDebug() << "AvatarActionHold::doKinematicUpdate -- no rigidBody"; + return; + } + + withWriteLock([&]{ + if (_kinematicSetVelocity) { + if (_previousSet) { + glm::vec3 positionalVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; + rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); + // back up along velocity a bit in order to smooth out a "vibrating" appearance + _positionalTarget -= positionalVelocity * deltaTimeStep / 2.0f; + } + } + + btTransform worldTrans = rigidBody->getWorldTransform(); + worldTrans.setOrigin(glmToBullet(_positionalTarget)); + worldTrans.setRotation(glmToBullet(_rotationalTarget)); + rigidBody->setWorldTransform(worldTrans); + + _previousPositionalTarget = _positionalTarget; + _previousRotationalTarget = _rotationalTarget; + _previousSet = true; + }); + + activateBody(); +} bool AvatarActionHold::updateArguments(QVariantMap arguments) { glm::vec3 relativePosition; @@ -82,6 +133,8 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { float timeScale; QString hand; QUuid holderID; + bool kinematic; + bool kinematicSetVelocity; bool needUpdate = false; bool somethingChanged = ObjectAction::updateArguments(arguments); @@ -114,11 +167,27 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { auto myAvatar = DependencyManager::get()->getMyAvatar(); holderID = myAvatar->getSessionUUID(); + ok = true; + kinematic = EntityActionInterface::extractBooleanArgument("hold", arguments, "kinematic", ok, false); + if (!ok) { + _kinematic = false; + } + + ok = true; + kinematicSetVelocity = EntityActionInterface::extractBooleanArgument("hold", arguments, + "kinematicSetVelocity", ok, false); + if (!ok) { + _kinematicSetVelocity = false; + } + if (somethingChanged || relativePosition != _relativePosition || relativeRotation != _relativeRotation || timeScale != _linearTimeScale || - hand != _hand) { + hand != _hand || + holderID != _holderID || + kinematic != _kinematic || + kinematicSetVelocity != _kinematicSetVelocity) { needUpdate = true; } }); @@ -131,6 +200,9 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { _linearTimeScale = glm::max(MIN_TIMESCALE, timeScale); _angularTimeScale = _linearTimeScale; _hand = hand; + _holderID = holderID; + _kinematic = kinematic; + _kinematicSetVelocity = kinematicSetVelocity; _active = true; auto ownerEntity = _ownerEntity.lock(); @@ -147,6 +219,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { QVariantMap AvatarActionHold::getArguments() { QVariantMap arguments = ObjectAction::getArguments(); withReadLock([&]{ + arguments["holderID"] = _holderID; arguments["relativePosition"] = glmToQMap(_relativePosition); arguments["relativeRotation"] = glmToQMap(_relativeRotation); arguments["timeScale"] = _linearTimeScale; @@ -156,9 +229,62 @@ QVariantMap AvatarActionHold::getArguments() { } QByteArray AvatarActionHold::serialize() const { - return ObjectActionSpring::serialize(); + QByteArray serializedActionArguments; + QDataStream dataStream(&serializedActionArguments, QIODevice::WriteOnly); + + withReadLock([&]{ + dataStream << ACTION_TYPE_HOLD; + dataStream << getID(); + dataStream << AvatarActionHold::holdVersion; + + dataStream << _holderID; + dataStream << _relativePosition; + dataStream << _relativeRotation; + dataStream << _linearTimeScale; + dataStream << _hand; + + dataStream << _expires + getEntityServerClockSkew(); + dataStream << _tag; + }); + + return serializedActionArguments; } void AvatarActionHold::deserialize(QByteArray serializedArguments) { - assert(false); + QDataStream dataStream(serializedArguments); + + EntityActionType type; + dataStream >> type; + assert(type == getType()); + + QUuid id; + dataStream >> id; + assert(id == getID()); + + uint16_t serializationVersion; + dataStream >> serializationVersion; + if (serializationVersion != AvatarActionHold::holdVersion) { + return; + } + + withWriteLock([&]{ + dataStream >> _holderID; + dataStream >> _relativePosition; + dataStream >> _relativeRotation; + dataStream >> _linearTimeScale; + _angularTimeScale = _linearTimeScale; + dataStream >> _hand; + + dataStream >> _expires; + _expires -= getEntityServerClockSkew(); + dataStream >> _tag; + + #if WANT_DEBUG + qDebug() << "deserialize AvatarActionHold: " << _holderID + << _relativePosition.x << _relativePosition.y << _relativePosition.z + << _hand << _expires; + #endif + + _active = true; + }); } diff --git a/interface/src/avatar/AvatarActionHold.h b/interface/src/avatar/AvatarActionHold.h index 840ba41c28..6badf97e9e 100644 --- a/interface/src/avatar/AvatarActionHold.h +++ b/interface/src/avatar/AvatarActionHold.h @@ -30,7 +30,7 @@ public: QByteArray serialize() const; virtual void deserialize(QByteArray serializedArguments); - virtual bool shouldSuppressLocationEdits() { return false; } + virtual bool shouldSuppressLocationEdits() { return true; } private: static const uint16_t holdVersion; @@ -38,6 +38,14 @@ private: glm::vec3 _relativePosition; glm::quat _relativeRotation; QString _hand; + QUuid _holderID; + + void doKinematicUpdate(float deltaTimeStep); + bool _kinematic { false }; + bool _kinematicSetVelocity { false }; + bool _previousSet { false }; + glm::vec3 _previousPositionalTarget; + glm::quat _previousRotationalTarget; }; #endif // hifi_AvatarActionHold_h diff --git a/interface/src/avatar/AvatarActionKinematicHold.cpp b/interface/src/avatar/AvatarActionKinematicHold.cpp deleted file mode 100644 index 680363877f..0000000000 --- a/interface/src/avatar/AvatarActionKinematicHold.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// -// AvatarActionKinematicHold.cpp -// interface/src/avatar/ -// -// Created by Seth Alves 2015-6-9 -// 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 "QVariantGLM.h" -#include "avatar/MyAvatar.h" -#include "avatar/AvatarManager.h" - -#include "AvatarActionKinematicHold.h" - -const uint16_t AvatarActionKinematicHold::holdVersion = 1; - -AvatarActionKinematicHold::AvatarActionKinematicHold(const QUuid& id, EntityItemPointer ownerEntity) : - ObjectActionSpring(id, ownerEntity), - _relativePosition(glm::vec3(0.0f)), - _relativeRotation(glm::quat()), - _hand("right"), - _mine(false), - _previousPositionalTarget(Vectors::ZERO), - _previousRotationalTarget(Quaternions::IDENTITY) -{ - _type = ACTION_TYPE_KINEMATIC_HOLD; - #if WANT_DEBUG - qDebug() << "AvatarActionKinematicHold::AvatarActionKinematicHold"; - #endif -} - -AvatarActionKinematicHold::~AvatarActionKinematicHold() { - #if WANT_DEBUG - qDebug() << "AvatarActionKinematicHold::~AvatarActionKinematicHold"; - #endif -} - -void AvatarActionKinematicHold::updateActionWorker(float deltaTimeStep) { - if (!_mine) { - // if a local script isn't updating this, then we are just getting spring-action data over the wire. - // let the super-class handle it. - ObjectActionSpring::updateActionWorker(deltaTimeStep); - return; - } - - assert(deltaTimeStep > 0.0f); - - glm::quat rotation; - glm::vec3 position; - glm::vec3 offset; - bool gotLock = withTryReadLock([&]{ - auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::vec3 palmPosition; - glm::quat palmRotation; - if (_hand == "right") { - palmPosition = myAvatar->getRightPalmPosition(); - palmRotation = myAvatar->getRightPalmRotation(); - } else { - palmPosition = myAvatar->getLeftPalmPosition(); - palmRotation = myAvatar->getLeftPalmRotation(); - } - - rotation = palmRotation * _relativeRotation; - offset = rotation * _relativePosition; - position = palmPosition + offset; - }); - - if (gotLock) { - gotLock = withTryWriteLock([&]{ - if (_positionalTarget != position || _rotationalTarget != rotation) { - _positionalTarget = position; - _rotationalTarget = rotation; - auto ownerEntity = _ownerEntity.lock(); - if (ownerEntity) { - ownerEntity->setActionDataDirty(true); - void* physicsInfo = ownerEntity->getPhysicsInfo(); - if (physicsInfo) { - ObjectMotionState* motionState = static_cast(physicsInfo); - btRigidBody* rigidBody = motionState ? motionState->getRigidBody() : nullptr; - if (!rigidBody) { - qDebug() << "ObjectActionSpring::updateActionWorker no rigidBody"; - return; - } - - if (_setVelocity) { - if (_previousSet) { - glm::vec3 positionalVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep; - rigidBody->setLinearVelocity(glmToBullet(positionalVelocity)); - // back up along velocity a bit in order to smooth out a "vibrating" appearance - _positionalTarget -= positionalVelocity * deltaTimeStep / 2.0f; - } - } - - btTransform worldTrans = rigidBody->getWorldTransform(); - worldTrans.setOrigin(glmToBullet(_positionalTarget)); - worldTrans.setRotation(glmToBullet(_rotationalTarget)); - rigidBody->setWorldTransform(worldTrans); - - _previousPositionalTarget = _positionalTarget; - _previousRotationalTarget = _rotationalTarget; - _previousSet = true; - } - } - } - }); - } - - if (gotLock) { - ObjectActionSpring::updateActionWorker(deltaTimeStep); - } -} - - -bool AvatarActionKinematicHold::updateArguments(QVariantMap arguments) { - if (!ObjectAction::updateArguments(arguments)) { - return false; - } - bool ok = true; - glm::vec3 relativePosition = - EntityActionInterface::extractVec3Argument("kinematic-hold", arguments, "relativePosition", ok, false); - if (!ok) { - relativePosition = _relativePosition; - } - - ok = true; - glm::quat relativeRotation = - EntityActionInterface::extractQuatArgument("kinematic-hold", arguments, "relativeRotation", ok, false); - if (!ok) { - relativeRotation = _relativeRotation; - } - - ok = true; - QString hand = - EntityActionInterface::extractStringArgument("kinematic-hold", arguments, "hand", ok, false); - if (!ok || !(hand == "left" || hand == "right")) { - hand = _hand; - } - - ok = true; - int setVelocity = - EntityActionInterface::extractIntegerArgument("kinematic-hold", arguments, "setVelocity", ok, false); - if (!ok) { - setVelocity = false; - } - - if (relativePosition != _relativePosition - || relativeRotation != _relativeRotation - || hand != _hand) { - withWriteLock([&] { - _relativePosition = relativePosition; - _relativeRotation = relativeRotation; - _hand = hand; - _setVelocity = setVelocity; - - _mine = true; - _active = true; - activateBody(); - }); - } - return true; -} - - -QVariantMap AvatarActionKinematicHold::getArguments() { - QVariantMap arguments = ObjectAction::getArguments(); - withReadLock([&]{ - if (!_mine) { - arguments = ObjectActionSpring::getArguments(); - return; - } - - arguments["relativePosition"] = glmToQMap(_relativePosition); - arguments["relativeRotation"] = glmToQMap(_relativeRotation); - arguments["setVelocity"] = _setVelocity; - arguments["hand"] = _hand; - }); - return arguments; -} - - -void AvatarActionKinematicHold::deserialize(QByteArray serializedArguments) { - if (!_mine) { - ObjectActionSpring::deserialize(serializedArguments); - } -} diff --git a/interface/src/avatar/AvatarActionKinematicHold.h b/interface/src/avatar/AvatarActionKinematicHold.h deleted file mode 100644 index b4fceab1e7..0000000000 --- a/interface/src/avatar/AvatarActionKinematicHold.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// AvatarActionKinematicHold.h -// interface/src/avatar/ -// -// Created by Seth Alves 2015-10-2 -// 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 -// - -#ifndef hifi_AvatarActionKinematicHold_h -#define hifi_AvatarActionKinematicHold_h - -#include - -#include -#include - -class AvatarActionKinematicHold : public ObjectActionSpring { -public: - AvatarActionKinematicHold(const QUuid& id, EntityItemPointer ownerEntity); - virtual ~AvatarActionKinematicHold(); - - virtual bool updateArguments(QVariantMap arguments); - virtual QVariantMap getArguments(); - - virtual void updateActionWorker(float deltaTimeStep); - - virtual void deserialize(QByteArray serializedArguments); - -private: - static const uint16_t holdVersion; - - glm::vec3 _relativePosition; - glm::quat _relativeRotation; - QString _hand; - bool _mine = false; - - bool _previousSet = false; - glm::vec3 _previousPositionalTarget; - glm::quat _previousRotationalTarget; - - bool _setVelocity = false; -}; - -#endif // hifi_AvatarActionKinematicHold_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 312b7cbf44..28c7941c52 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -229,6 +229,12 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { Model::simulate(deltaTime, fullUpdate); + // let rig compute the model offset + glm::vec3 modelOffset; + if (_rig->getModelOffset(modelOffset)) { + setOffset(modelOffset); + } + if (!isActive() || !_owningAvatar->isMyAvatar()) { return; // only simulate for own avatar } @@ -246,12 +252,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { Hand* hand = _owningAvatar->getHand(); hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - // let rig compute the model offset - glm::vec3 modelOffset; - if (_rig->getModelOffset(modelOffset)) { - setOffset(modelOffset); - } - // Don't Relax toward hand positions when in animGraph mode. if (!_rig->getEnableAnimGraph()) { const float HAND_RESTORATION_RATE = 0.25f; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 25f28a3f64..6fd6f5cbf9 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1238,12 +1238,18 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { } } +void Rig::makeAnimSkeleton(const FBXGeometry& fbxGeometry) { + if (!_animSkeleton) { + _animSkeleton = std::make_shared(fbxGeometry); + } +} + void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) { if (!_enableAnimGraph) { return; } - _animSkeleton = std::make_shared(fbxGeometry); + makeAnimSkeleton(fbxGeometry); // load the anim graph _animLoader.reset(new AnimNodeLoader(url)); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 71c27e7213..c9c42cbbb6 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -193,6 +193,7 @@ public: virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority) = 0; + void makeAnimSkeleton(const FBXGeometry& fbxGeometry); void initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry); AnimNode::ConstPointer getAnimNode() const { return _animNode; } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 8a973c88a9..e604bdb925 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -298,18 +298,23 @@ void RenderableModelEntityItem::render(RenderArgs* args) { } bool movingOrAnimating = isMoving() || isAnimatingSomething(); - if ((movingOrAnimating || _needsInitialSimulation) && _model->isActive() && _dimensionsInitialized) { + if ((movingOrAnimating || + _needsInitialSimulation || + _model->getTranslation() != getPosition() || + _model->getRotation() != getRotation() || + _model->getRegistrationPoint() != getRegistrationPoint()) + && _model->isActive() && _dimensionsInitialized) { _model->setScaleToFit(true, getDimensions()); _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); _model->setRotation(getRotation()); _model->setTranslation(getPosition()); - + // make sure to simulate so everything gets set up correctly for rendering { PerformanceTimer perfTimer("_model->simulate"); _model->simulate(0.0f); } - + _needsInitialSimulation = false; } } diff --git a/libraries/entities/src/EntityActionInterface.cpp b/libraries/entities/src/EntityActionInterface.cpp index d874646b4b..549aacbd0a 100644 --- a/libraries/entities/src/EntityActionInterface.cpp +++ b/libraries/entities/src/EntityActionInterface.cpp @@ -100,9 +100,6 @@ EntityActionType EntityActionInterface::actionTypeFromString(QString actionTypeS if (normalizedActionTypeString == "hold") { return ACTION_TYPE_HOLD; } - if (normalizedActionTypeString == "kinematichold") { - return ACTION_TYPE_KINEMATIC_HOLD; - } qDebug() << "Warning -- EntityActionInterface::actionTypeFromString got unknown action-type name" << actionTypeString; return ACTION_TYPE_NONE; @@ -118,8 +115,6 @@ QString EntityActionInterface::actionTypeToString(EntityActionType actionType) { return "spring"; case ACTION_TYPE_HOLD: return "hold"; - case ACTION_TYPE_KINEMATIC_HOLD: - return "kinematic-hold"; } assert(false); return "none"; @@ -280,12 +275,23 @@ QString EntityActionInterface::extractStringArgument(QString objectName, QVarian ok = false; return ""; } - - QVariant vV = arguments[argumentName]; - QString v = vV.toString(); - return v; + return arguments[argumentName].toString(); } +bool EntityActionInterface::extractBooleanArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required) { + if (!arguments.contains(argumentName)) { + if (required) { + qDebug() << objectName << "requires argument:" << argumentName; + } + ok = false; + return false; + } + return arguments[argumentName].toBool(); +} + + + QDataStream& operator<<(QDataStream& stream, const EntityActionType& entityActionType) { return stream << (quint16)entityActionType; diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index 49739e7da9..d97b5e8106 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -23,8 +23,7 @@ enum EntityActionType { ACTION_TYPE_NONE = 0, ACTION_TYPE_OFFSET = 1000, ACTION_TYPE_SPRING = 2000, - ACTION_TYPE_HOLD = 3000, - ACTION_TYPE_KINEMATIC_HOLD = 4000 + ACTION_TYPE_HOLD = 3000 }; @@ -66,6 +65,8 @@ public: QString argumentName, bool& ok, bool required = true); static QString extractStringArgument(QString objectName, QVariantMap arguments, QString argumentName, bool& ok, bool required = true); + static bool extractBooleanArgument(QString objectName, QVariantMap arguments, + QString argumentName, bool& ok, bool required = true); protected: virtual glm::vec3 getPosition() = 0; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 5b262b273b..1fd9acc1e2 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -342,6 +342,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef "ERROR CASE...args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU"; return 0; } + setSourceUUID(args.sourceUUID); args.entitiesPerPacket++; @@ -1300,6 +1301,9 @@ void EntityItem::computeShapeInfo(ShapeInfo& info) { } void EntityItem::updatePosition(const glm::vec3& value) { + if (shouldSuppressLocationEdits()) { + return; + } auto delta = glm::distance(getPosition(), value); if (delta > IGNORE_POSITION_DELTA) { _dirtyFlags |= Simulation::DIRTY_POSITION; @@ -1322,6 +1326,9 @@ void EntityItem::updateDimensions(const glm::vec3& value) { } void EntityItem::updateRotation(const glm::quat& rotation) { + if (shouldSuppressLocationEdits()) { + return; + } if (getRotation() != rotation) { setRotation(rotation); @@ -1362,6 +1369,9 @@ void EntityItem::updateMass(float mass) { } void EntityItem::updateVelocity(const glm::vec3& value) { + if (shouldSuppressLocationEdits()) { + return; + } auto delta = glm::distance(_velocity, value); if (delta > IGNORE_LINEAR_VELOCITY_DELTA) { _dirtyFlags |= Simulation::DIRTY_LINEAR_VELOCITY; @@ -1398,6 +1408,9 @@ void EntityItem::updateGravity(const glm::vec3& value) { } void EntityItem::updateAngularVelocity(const glm::vec3& value) { + if (shouldSuppressLocationEdits()) { + return; + } auto delta = glm::distance(_angularVelocity, value); if (delta > IGNORE_ANGULAR_VELOCITY_DELTA) { _dirtyFlags |= Simulation::DIRTY_ANGULAR_VELOCITY; @@ -1534,6 +1547,8 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi if (success) { _allActionsDataCache = newDataCache; _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; + } else { + qDebug() << "EntityItem::addActionInternal -- serializeActions failed"; } return success; } @@ -1628,6 +1643,7 @@ void EntityItem::deserializeActionsInternal() { quint64 now = usecTimestampNow(); if (!_element) { + qDebug() << "EntityItem::deserializeActionsInternal -- no _element"; return; } @@ -1670,6 +1686,8 @@ void EntityItem::deserializeActionsInternal() { if (action) { entity->addActionInternal(simulation, action); action->locallyAddedButNotYetReceived = false; + } else { + qDebug() << "EntityItem::deserializeActionsInternal -- action creation failed"; } } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index f438e3f28b..858dc7e326 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -409,6 +409,10 @@ public: void setActionDataDirty(bool value) const { _actionDataDirty = value; } bool shouldSuppressLocationEdits() const; + void setSourceUUID(const QUuid& sourceUUID) { _sourceUUID = sourceUUID; } + const QUuid& getSourceUUID() const { return _sourceUUID; } + bool matchesSourceUUID(const QUuid& sourceUUID) const { return _sourceUUID == sourceUUID; } + protected: const QByteArray getActionDataInternal() const; @@ -509,6 +513,8 @@ protected: // _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag static quint64 _rememberDeletedActionTime; mutable QHash _previouslyDeletedActions; + + QUuid _sourceUUID; /// the server node UUID we came from }; #endif // hifi_EntityItem_h diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c458c2e1f8..6832b8daff 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include #include #include @@ -186,10 +188,8 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { measureBodyAcceleration(); _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset()); _entity->setRotation(bulletToGLM(worldTrans.getRotation())); - _entity->setVelocity(getBodyLinearVelocity()); _entity->setAngularVelocity(getBodyAngularVelocity()); - _entity->setLastSimulated(usecTimestampNow()); if (_entity->getSimulatorID().isNull()) { @@ -248,7 +248,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) { btTransform xform = _body->getWorldTransform(); _serverPosition = bulletToGLM(xform.getOrigin()); _serverRotation = bulletToGLM(xform.getRotation()); - _serverVelocity = getBodyLinearVelocity(); + _serverVelocity = getBodyLinearVelocityGTSigma(); _serverAngularVelocity = bulletToGLM(_body->getAngularVelocity()); _lastStep = simulationStep; _serverActionData = _entity->getActionData(); @@ -547,7 +547,7 @@ void EntityMotionState::bump(quint8 priority) { void EntityMotionState::resetMeasuredBodyAcceleration() { _lastMeasureStep = ObjectMotionState::getWorldSimulationStep(); if (_body) { - _lastVelocity = getBodyLinearVelocity(); + _lastVelocity = getBodyLinearVelocityGTSigma(); } else { _lastVelocity = glm::vec3(0.0f); } @@ -566,7 +566,8 @@ void EntityMotionState::measureBodyAcceleration() { // Note: the integration equation for velocity uses damping: v1 = (v0 + a * dt) * (1 - D)^dt // hence the equation for acceleration is: a = (v1 / (1 - D)^dt - v0) / dt - glm::vec3 velocity = getBodyLinearVelocity(); + glm::vec3 velocity = getBodyLinearVelocityGTSigma(); + _measuredAcceleration = (velocity / powf(1.0f - _body->getLinearDamping(), dt) - _lastVelocity) * invDt; _lastVelocity = velocity; if (numSubsteps > PHYSICS_ENGINE_MAX_NUM_SUBSTEPS) { diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 2f0de6d0ab..ce4ebfd22f 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -62,6 +62,22 @@ void ObjectAction::updateAction(btCollisionWorld* collisionWorld, btScalar delta updateActionWorker(deltaTimeStep); } +int ObjectAction::getEntityServerClockSkew() const { + auto nodeList = DependencyManager::get(); + + auto ownerEntity = _ownerEntity.lock(); + if (!ownerEntity) { + return 0; + } + + const QUuid& entityServerNodeID = ownerEntity->getSourceUUID(); + auto entityServerNode = nodeList->nodeWithUUID(entityServerNodeID); + if (entityServerNode) { + return entityServerNode->getClockSkewUsec(); + } + return 0; +} + bool ObjectAction::updateArguments(QVariantMap arguments) { bool somethingChanged = false; diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 5c29ac9892..98e58475c6 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -50,6 +50,8 @@ public: protected: + int getEntityServerClockSkew() const; + virtual btRigidBody* getRigidBody(); virtual glm::vec3 getPosition(); virtual void setPosition(glm::vec3 position); diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index d01178dcc3..b6edf22ffc 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -160,7 +160,7 @@ QByteArray ObjectActionOffset::serialize() const { dataStream << _linearDistance; dataStream << _linearTimeScale; dataStream << _positionalTargetSet; - dataStream << _expires; + dataStream << _expires + getEntityServerClockSkew(); dataStream << _tag; }); @@ -190,6 +190,7 @@ void ObjectActionOffset::deserialize(QByteArray serializedArguments) { dataStream >> _linearTimeScale; dataStream >> _positionalTargetSet; dataStream >> _expires; + _expires -= getEntityServerClockSkew(); dataStream >> _tag; _active = true; }); diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp index a99cfdd747..a74756eb53 100644 --- a/libraries/physics/src/ObjectActionSpring.cpp +++ b/libraries/physics/src/ObjectActionSpring.cpp @@ -198,7 +198,7 @@ QByteArray ObjectActionSpring::serialize() const { dataStream << _rotationalTarget; dataStream << _angularTimeScale; dataStream << _rotationalTargetSet; - dataStream << _expires; + dataStream << _expires + getEntityServerClockSkew(); dataStream << _tag; }); @@ -233,6 +233,7 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) { dataStream >> _rotationalTargetSet; dataStream >> _expires; + _expires -= getEntityServerClockSkew(); dataStream >> _tag; _active = true; diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index d2c152d876..4f3d0396c6 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -80,17 +80,20 @@ void ObjectMotionState::setBodyGravity(const glm::vec3& gravity) const { } glm::vec3 ObjectMotionState::getBodyLinearVelocity() const { - // returns the body's velocity unless it is moving too slow in which case returns zero - btVector3 velocity = _body->getLinearVelocity(); + return bulletToGLM(_body->getLinearVelocity()); +} +glm::vec3 ObjectMotionState::getBodyLinearVelocityGTSigma() const { // NOTE: the threshold to use here relates to the linear displacement threshold (dX) for sending updates // to objects that are tracked server-side (e.g. entities which use dX = 2mm). Hence an object moving // just under this velocity threshold would trigger an update about V/dX times per second. const float MIN_LINEAR_SPEED_SQUARED = 0.0036f; // 6 mm/sec - if (velocity.length2() < MIN_LINEAR_SPEED_SQUARED) { + + glm::vec3 velocity = bulletToGLM(_body->getLinearVelocity()); + if (glm::length2(velocity) < MIN_LINEAR_SPEED_SQUARED) { velocity *= 0.0f; } - return bulletToGLM(velocity); + return velocity; } glm::vec3 ObjectMotionState::getObjectLinearVelocityChange() const { diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index ec98f8858f..450ac34a90 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -89,6 +89,7 @@ public: void setBodyGravity(const glm::vec3& gravity) const; glm::vec3 getBodyLinearVelocity() const; + glm::vec3 getBodyLinearVelocityGTSigma() const; glm::vec3 getBodyAngularVelocity() const; virtual glm::vec3 getObjectLinearVelocityChange() const; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 38f5ffdabe..6aae7ad1cb 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1147,6 +1147,8 @@ void Model::segregateMeshGroups() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const std::vector>& networkMeshes = _geometry->getMeshes(); + _rig->makeAnimSkeleton(geometry); + // all of our mesh vectors must match in size if ((int)networkMeshes.size() != geometry.meshes.size() || geometry.meshes.size() != _meshStates.size()) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e3a9ce9ac3..f5d5f40363 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -198,6 +198,10 @@ public: return ((index < 0) && (index >= _blendshapeCoefficients.size())) ? 0.0f : _blendshapeCoefficients.at(index); } + virtual RigPointer getRig() const { return _rig; } + + const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } + protected: void setPupilDilation(float dilation) { _pupilDilation = dilation; }