Merge pull request #7658 from sethalves/grab-fixes

Grab fixes
This commit is contained in:
Andrew Meadows 2016-05-09 15:30:37 -07:00
commit ffc3123467
11 changed files with 220 additions and 110 deletions

View file

@ -93,13 +93,13 @@ void AvatarActionHold::prepareForPhysicsSimulation() {
activateBody(true); activateBody(true);
} }
std::shared_ptr<Avatar> AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position, bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity) { glm::vec3& linearVelocity, glm::vec3& angularVelocity) {
auto avatarManager = DependencyManager::get<AvatarManager>(); auto avatarManager = DependencyManager::get<AvatarManager>();
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID)); auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
if (!holdingAvatar) { if (!holdingAvatar) {
return holdingAvatar; return false;;
} }
withReadLock([&]{ withReadLock([&]{
@ -171,62 +171,17 @@ std::shared_ptr<Avatar> AvatarActionHold::getTarget(float deltaTimeStep, glm::qu
linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition); linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition);
}); });
return holdingAvatar; return true;
} }
void AvatarActionHold::updateActionWorker(float deltaTimeStep) { void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
glm::quat rotation; if (_kinematic) {
glm::vec3 position; if (prepareForSpringUpdate(deltaTimeStep)) {
glm::vec3 linearVelocity;
glm::vec3 angularVelocity;
bool valid = false;
int holdCount = 0;
auto ownerEntity = _ownerEntity.lock();
if (!ownerEntity) {
return;
}
QList<EntityActionPointer> holdActions = ownerEntity->getActionsOfType(ACTION_TYPE_HOLD);
foreach (EntityActionPointer action, holdActions) {
std::shared_ptr<AvatarActionHold> holdAction = std::static_pointer_cast<AvatarActionHold>(action);
glm::quat rotationForAction;
glm::vec3 positionForAction;
glm::vec3 linearVelocityForAction, angularVelocityForAction;
std::shared_ptr<Avatar> holdingAvatar = holdAction->getTarget(deltaTimeStep, rotationForAction, positionForAction, linearVelocityForAction, angularVelocityForAction);
if (holdingAvatar) {
holdCount ++;
if (holdAction.get() == this) {
// only use the rotation for this action
valid = true;
rotation = rotationForAction;
}
position += positionForAction;
linearVelocity += linearVelocityForAction;
angularVelocity += angularVelocityForAction;
}
}
if (valid && holdCount > 0) {
position /= holdCount;
linearVelocity /= holdCount;
angularVelocity /= holdCount;
withWriteLock([&]{
_positionalTarget = position;
_rotationalTarget = rotation;
_linearVelocityTarget = linearVelocity;
_angularVelocityTarget = angularVelocity;
_positionalTargetSet = true;
_rotationalTargetSet = true;
_active = true;
});
if (_kinematic) {
doKinematicUpdate(deltaTimeStep); doKinematicUpdate(deltaTimeStep);
} else {
forceBodyNonStatic();
ObjectActionSpring::updateActionWorker(deltaTimeStep);
} }
} else {
forceBodyNonStatic();
ObjectActionSpring::updateActionWorker(deltaTimeStep);
} }
} }

View file

@ -36,8 +36,8 @@ public:
virtual bool shouldSuppressLocationEdits() override { return _active && !_ownerEntity.expired(); } virtual bool shouldSuppressLocationEdits() override { return _active && !_ownerEntity.expired(); }
bool getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); bool getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
std::shared_ptr<Avatar> getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position, virtual bool getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity); glm::vec3& linearVelocity, glm::vec3& angularVelocity) override;
virtual void prepareForPhysicsSimulation() override; virtual void prepareForPhysicsSimulation() override;
@ -51,9 +51,6 @@ private:
QString _hand { "right" }; QString _hand { "right" };
QUuid _holderID; QUuid _holderID;
glm::vec3 _linearVelocityTarget;
glm::vec3 _angularVelocityTarget;
bool _kinematic { false }; bool _kinematic { false };
bool _kinematicSetVelocity { false }; bool _kinematicSetVelocity { false };
bool _previousSet { false }; bool _previousSet { false };

View file

@ -57,8 +57,6 @@ public:
virtual bool isMine() { return _isMine; } virtual bool isMine() { return _isMine; }
virtual void setIsMine(bool value) { _isMine = value; } virtual void setIsMine(bool value) { _isMine = value; }
bool locallyAddedButNotYetReceived = false;
virtual bool shouldSuppressLocationEdits() { return false; } virtual bool shouldSuppressLocationEdits() { return false; }
virtual void prepareForPhysicsSimulation() { } virtual void prepareForPhysicsSimulation() { }

View file

@ -726,8 +726,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription); READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData); READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData);
READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, setParentID); { // parentID and parentJointIndex are also protected by simulation ownership
READ_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex); bool oldOverwrite = overwriteLocalData;
overwriteLocalData = overwriteLocalData && !weOwnSimulation;
READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, setParentID);
READ_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex);
overwriteLocalData = oldOverwrite;
}
READ_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, AACube, setQueryAACube); READ_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, AACube, setQueryAACube);
@ -1717,16 +1722,31 @@ void EntityItem::setPendingOwnershipPriority(quint8 priority, const quint64& tim
_simulationOwner.setPendingPriority(priority, timestamp); _simulationOwner.setPendingPriority(priority, timestamp);
} }
QString EntityItem::actionsToDebugString() {
QString result;
QVector<QByteArray> serializedActions;
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
const QUuid id = i.key();
EntityActionPointer action = _objectActions[id];
EntityActionType actionType = action->getType();
result += QString("") + actionType + ":" + action->getID().toString() + " ";
i++;
}
return result;
}
bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) { bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) {
bool result; bool result;
withWriteLock([&] { withWriteLock([&] {
checkWaitingToRemove(simulation); checkWaitingToRemove(simulation);
result = addActionInternal(simulation, action); result = addActionInternal(simulation, action);
if (!result) { if (result) {
removeActionInternal(action->getID()); action->setIsMine(true);
_actionDataDirty = true;
} else { } else {
action->locallyAddedButNotYetReceived = true; removeActionInternal(action->getID());
} }
}); });
@ -1800,6 +1820,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
EntityActionPointer action = _objectActions[actionID]; EntityActionPointer action = _objectActions[actionID];
action->setOwnerEntity(nullptr); action->setOwnerEntity(nullptr);
action->setIsMine(false);
_objectActions.remove(actionID); _objectActions.remove(actionID);
if (simulation) { if (simulation) {
@ -1880,7 +1901,6 @@ void EntityItem::deserializeActionsInternal() {
if (!action->isMine()) { if (!action->isMine()) {
action->deserialize(serializedAction); action->deserialize(serializedAction);
} }
action->locallyAddedButNotYetReceived = false;
updated << actionID; updated << actionID;
} else { } else {
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>(); auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
@ -1888,7 +1908,6 @@ void EntityItem::deserializeActionsInternal() {
EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction); EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction);
if (action) { if (action) {
entity->addActionInternal(simulation, action); entity->addActionInternal(simulation, action);
action->locallyAddedButNotYetReceived = false;
updated << actionID; updated << actionID;
} else { } else {
static QString repeatedMessage = static QString repeatedMessage =
@ -1906,8 +1925,12 @@ void EntityItem::deserializeActionsInternal() {
QUuid id = i.key(); QUuid id = i.key();
if (!updated.contains(id)) { if (!updated.contains(id)) {
EntityActionPointer action = i.value(); EntityActionPointer action = i.value();
// if we've just added this action, don't remove it due to lack of mention in an incoming packet.
if (! action->locallyAddedButNotYetReceived) { if (action->isMine()) {
// we just received an update that didn't include one of our actions. tell the server about it (again).
setActionDataNeedsTransmit(true);
} else {
// don't let someone else delete my action.
_actionsToRemove << id; _actionsToRemove << id;
_previouslyDeletedActions.insert(id, now); _previouslyDeletedActions.insert(id, now);
} }

View file

@ -382,6 +382,7 @@ public:
void grabSimulationOwnership(); void grabSimulationOwnership();
void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; }
QString actionsToDebugString();
bool addAction(EntitySimulation* simulation, EntityActionPointer action); bool addAction(EntitySimulation* simulation, EntityActionPointer action);
bool updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments); bool updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments);
bool removeAction(EntitySimulation* simulation, const QUuid& actionID); bool removeAction(EntitySimulation* simulation, const QUuid& actionID);

View file

@ -1615,7 +1615,7 @@ void EntityItemProperties::setSimulationOwner(const QByteArray& data) {
QList<QString> EntityItemProperties::listChangedProperties() { QList<QString> EntityItemProperties::listChangedProperties() {
QList<QString> out; QList<QString> out;
if (containsPositionChange()) { if (containsPositionChange()) {
out += "posistion"; out += "position";
} }
if (dimensionsChanged()) { if (dimensionsChanged()) {
out += "dimensions"; out += "dimensions";

View file

@ -212,6 +212,8 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
properties.setVelocityChanged(false); properties.setVelocityChanged(false);
properties.setAngularVelocityChanged(false); properties.setAngularVelocityChanged(false);
properties.setAccelerationChanged(false); properties.setAccelerationChanged(false);
properties.setParentIDChanged(false);
properties.setParentJointIndexChanged(false);
if (wantTerseEditLogging()) { if (wantTerseEditLogging()) {
qCDebug(entities) << (senderNode ? senderNode->getUUID() : "null") << "physical edits suppressed"; qCDebug(entities) << (senderNode ? senderNode->getUUID() : "null") << "physical edits suppressed";
@ -843,6 +845,14 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
QString::number((int)center.y) + "," + QString::number((int)center.y) + "," +
QString::number((int)center.z); QString::number((int)center.z);
} }
if (properties.positionChanged()) {
int index = changedProperties.indexOf("position");
glm::vec3 pos = properties.getPosition();
changedProperties[index] = QString("position:") +
QString::number((int)pos.x) + "," +
QString::number((int)pos.y) + "," +
QString::number((int)pos.z);
}
} }
int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,

View file

@ -21,9 +21,11 @@ const uint16_t ObjectActionSpring::springVersion = 1;
ObjectActionSpring::ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity) : ObjectActionSpring::ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity) :
ObjectAction(ACTION_TYPE_SPRING, id, ownerEntity), ObjectAction(ACTION_TYPE_SPRING, id, ownerEntity),
_positionalTarget(glm::vec3(0.0f)), _positionalTarget(glm::vec3(0.0f)),
_desiredPositionalTarget(glm::vec3(0.0f)),
_linearTimeScale(FLT_MAX), _linearTimeScale(FLT_MAX),
_positionalTargetSet(true), _positionalTargetSet(true),
_rotationalTarget(glm::quat()), _rotationalTarget(glm::quat()),
_desiredRotationalTarget(glm::quat()),
_angularTimeScale(FLT_MAX), _angularTimeScale(FLT_MAX),
_rotationalTargetSet(true) { _rotationalTargetSet(true) {
#if WANT_DEBUG #if WANT_DEBUG
@ -37,9 +39,81 @@ ObjectActionSpring::~ObjectActionSpring() {
#endif #endif
} }
bool ObjectActionSpring::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity) {
rotation = _desiredRotationalTarget;
position = _desiredPositionalTarget;
linearVelocity = glm::vec3();
angularVelocity = glm::vec3();
return true;
}
bool ObjectActionSpring::prepareForSpringUpdate(btScalar deltaTimeStep) {
auto ownerEntity = _ownerEntity.lock();
if (!ownerEntity) {
return false;
}
glm::quat rotation;
glm::vec3 position;
glm::vec3 linearVelocity;
glm::vec3 angularVelocity;
bool valid = false;
int springCount = 0;
QList<EntityActionPointer> springDerivedActions;
springDerivedActions.append(ownerEntity->getActionsOfType(ACTION_TYPE_SPRING));
springDerivedActions.append(ownerEntity->getActionsOfType(ACTION_TYPE_HOLD));
foreach (EntityActionPointer action, springDerivedActions) {
std::shared_ptr<ObjectActionSpring> springAction = std::static_pointer_cast<ObjectActionSpring>(action);
glm::quat rotationForAction;
glm::vec3 positionForAction;
glm::vec3 linearVelocityForAction, angularVelocityForAction;
bool success = springAction->getTarget(deltaTimeStep, rotationForAction,
positionForAction, linearVelocityForAction,
angularVelocityForAction);
if (success) {
springCount ++;
if (springAction.get() == this) {
// only use the rotation for this action
valid = true;
rotation = rotationForAction;
}
position += positionForAction;
linearVelocity += linearVelocityForAction;
angularVelocity += angularVelocityForAction;
}
}
if (valid && springCount > 0) {
position /= springCount;
linearVelocity /= springCount;
angularVelocity /= springCount;
withWriteLock([&]{
_positionalTarget = position;
_rotationalTarget = rotation;
_linearVelocityTarget = linearVelocity;
_angularVelocityTarget = angularVelocity;
_positionalTargetSet = true;
_rotationalTargetSet = true;
_active = true;
});
}
return valid;
}
void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) { void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
// don't risk hanging the thread running the physics simulation if (!prepareForSpringUpdate(deltaTimeStep)) {
auto lockResult = withTryReadLock([&]{ return;
}
withReadLock([&]{
auto ownerEntity = _ownerEntity.lock(); auto ownerEntity = _ownerEntity.lock();
if (!ownerEntity) { if (!ownerEntity) {
return; return;
@ -65,6 +139,7 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
float speed = glm::min(offsetLength / _linearTimeScale, SPRING_MAX_SPEED); float speed = glm::min(offsetLength / _linearTimeScale, SPRING_MAX_SPEED);
targetVelocity = (-speed / offsetLength) * offset; targetVelocity = (-speed / offsetLength) * offset;
if (speed > rigidBody->getLinearSleepingThreshold()) { if (speed > rigidBody->getLinearSleepingThreshold()) {
forceBodyNonStatic();
rigidBody->activate(); rigidBody->activate();
} }
} }
@ -101,9 +176,6 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
rigidBody->setAngularVelocity(targetVelocity); rigidBody->setAngularVelocity(targetVelocity);
} }
}); });
if (!lockResult) {
qDebug() << "ObjectActionSpring::updateActionWorker lock failed";
}
} }
const float MIN_TIMESCALE = 0.1f; const float MIN_TIMESCALE = 0.1f;
@ -122,7 +194,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
bool ok = true; bool ok = true;
positionalTarget = EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false); positionalTarget = EntityActionInterface::extractVec3Argument("spring action", arguments, "targetPosition", ok, false);
if (!ok) { if (!ok) {
positionalTarget = _positionalTarget; positionalTarget = _desiredPositionalTarget;
} }
ok = true; ok = true;
linearTimeScale = EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false); linearTimeScale = EntityActionInterface::extractFloatArgument("spring action", arguments, "linearTimeScale", ok, false);
@ -133,7 +205,7 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
ok = true; ok = true;
rotationalTarget = EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false); rotationalTarget = EntityActionInterface::extractQuatArgument("spring action", arguments, "targetRotation", ok, false);
if (!ok) { if (!ok) {
rotationalTarget = _rotationalTarget; rotationalTarget = _desiredRotationalTarget;
} }
ok = true; ok = true;
@ -144,9 +216,9 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
} }
if (somethingChanged || if (somethingChanged ||
positionalTarget != _positionalTarget || positionalTarget != _desiredPositionalTarget ||
linearTimeScale != _linearTimeScale || linearTimeScale != _linearTimeScale ||
rotationalTarget != _rotationalTarget || rotationalTarget != _desiredRotationalTarget ||
angularTimeScale != _angularTimeScale) { angularTimeScale != _angularTimeScale) {
// something changed // something changed
needUpdate = true; needUpdate = true;
@ -155,9 +227,9 @@ bool ObjectActionSpring::updateArguments(QVariantMap arguments) {
if (needUpdate) { if (needUpdate) {
withWriteLock([&] { withWriteLock([&] {
_positionalTarget = positionalTarget; _desiredPositionalTarget = positionalTarget;
_linearTimeScale = glm::max(MIN_TIMESCALE, glm::abs(linearTimeScale)); _linearTimeScale = glm::max(MIN_TIMESCALE, glm::abs(linearTimeScale));
_rotationalTarget = rotationalTarget; _desiredRotationalTarget = rotationalTarget;
_angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale)); _angularTimeScale = glm::max(MIN_TIMESCALE, glm::abs(angularTimeScale));
_active = true; _active = true;
@ -177,9 +249,9 @@ QVariantMap ObjectActionSpring::getArguments() {
QVariantMap arguments = ObjectAction::getArguments(); QVariantMap arguments = ObjectAction::getArguments();
withReadLock([&] { withReadLock([&] {
arguments["linearTimeScale"] = _linearTimeScale; arguments["linearTimeScale"] = _linearTimeScale;
arguments["targetPosition"] = glmToQMap(_positionalTarget); arguments["targetPosition"] = glmToQMap(_desiredPositionalTarget);
arguments["targetRotation"] = glmToQMap(_rotationalTarget); arguments["targetRotation"] = glmToQMap(_desiredRotationalTarget);
arguments["angularTimeScale"] = _angularTimeScale; arguments["angularTimeScale"] = _angularTimeScale;
}); });
return arguments; return arguments;
@ -194,10 +266,10 @@ QByteArray ObjectActionSpring::serialize() const {
dataStream << ObjectActionSpring::springVersion; dataStream << ObjectActionSpring::springVersion;
withReadLock([&] { withReadLock([&] {
dataStream << _positionalTarget; dataStream << _desiredPositionalTarget;
dataStream << _linearTimeScale; dataStream << _linearTimeScale;
dataStream << _positionalTargetSet; dataStream << _positionalTargetSet;
dataStream << _rotationalTarget; dataStream << _desiredRotationalTarget;
dataStream << _angularTimeScale; dataStream << _angularTimeScale;
dataStream << _rotationalTargetSet; dataStream << _rotationalTargetSet;
dataStream << localTimeToServerTime(_expires); dataStream << localTimeToServerTime(_expires);
@ -226,11 +298,11 @@ void ObjectActionSpring::deserialize(QByteArray serializedArguments) {
} }
withWriteLock([&] { withWriteLock([&] {
dataStream >> _positionalTarget; dataStream >> _desiredPositionalTarget;
dataStream >> _linearTimeScale; dataStream >> _linearTimeScale;
dataStream >> _positionalTargetSet; dataStream >> _positionalTargetSet;
dataStream >> _rotationalTarget; dataStream >> _desiredRotationalTarget;
dataStream >> _angularTimeScale; dataStream >> _angularTimeScale;
dataStream >> _rotationalTargetSet; dataStream >> _rotationalTargetSet;

View file

@ -27,16 +27,26 @@ public:
virtual QByteArray serialize() const override; virtual QByteArray serialize() const override;
virtual void deserialize(QByteArray serializedArguments) override; virtual void deserialize(QByteArray serializedArguments) override;
virtual bool getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity);
protected: protected:
static const uint16_t springVersion; static const uint16_t springVersion;
glm::vec3 _positionalTarget; glm::vec3 _positionalTarget;
glm::vec3 _desiredPositionalTarget;
float _linearTimeScale; float _linearTimeScale;
bool _positionalTargetSet; bool _positionalTargetSet;
glm::quat _rotationalTarget; glm::quat _rotationalTarget;
glm::quat _desiredRotationalTarget;
float _angularTimeScale; float _angularTimeScale;
bool _rotationalTargetSet; bool _rotationalTargetSet;
glm::vec3 _linearVelocityTarget;
glm::vec3 _angularVelocityTarget;
virtual bool prepareForSpringUpdate(btScalar deltaTimeStep);
}; };
#endif // hifi_ObjectActionSpring_h #endif // hifi_ObjectActionSpring_h

View file

@ -29,6 +29,10 @@ var IDENTITY_QUAT = {
var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with handControllerGrab.js var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with handControllerGrab.js
var GRAB_USER_DATA_KEY = "grabKey"; // shared with handControllerGrab.js var GRAB_USER_DATA_KEY = "grabKey"; // shared with handControllerGrab.js
var MSECS_PER_SEC = 1000.0;
var HEART_BEAT_INTERVAL = 5 * MSECS_PER_SEC;
var HEART_BEAT_TIMEOUT = 15 * MSECS_PER_SEC;
var DEFAULT_GRABBABLE_DATA = { var DEFAULT_GRABBABLE_DATA = {
grabbable: true, grabbable: true,
invertSolidWhileHeld: false invertSolidWhileHeld: false
@ -335,6 +339,9 @@ Grabber.prototype.pressEvent = function(event) {
mouse.startDrag(event); mouse.startDrag(event);
var now = Date.now();
this.lastHeartBeat = 0;
var clickedEntity = pickResults.entityID; var clickedEntity = pickResults.entityID;
var entityProperties = Entities.getEntityProperties(clickedEntity) var entityProperties = Entities.getEntityProperties(clickedEntity)
this.startPosition = entityProperties.position; this.startPosition = entityProperties.position;
@ -376,7 +383,16 @@ Grabber.prototype.pressEvent = function(event) {
if(!entityIsGrabbedByOther(this.entityID)){ if(!entityIsGrabbedByOther(this.entityID)){
this.moveEvent(event); this.moveEvent(event);
} }
var args = "mouse";
Entities.callEntityMethod(this.entityID, "startDistanceGrab", args);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'grab',
grabbedEntity: this.entityID
}));
// TODO: play sounds again when we aren't leaking AudioInjector threads // TODO: play sounds again when we aren't leaking AudioInjector threads
//Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME }); //Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME });
} }
@ -394,17 +410,41 @@ Grabber.prototype.releaseEvent = function(event) {
beacon.disable(); beacon.disable();
var args = "mouse";
Entities.callEntityMethod(this.entityID, "releaseGrab", args);
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'release',
grabbedEntity: this.entityID,
joint: "mouse"
}));
// TODO: play sounds again when we aren't leaking AudioInjector threads // TODO: play sounds again when we aren't leaking AudioInjector threads
//Audio.playSound(releaseSound, { position: entityProperties.position, volume: VOLUME }); //Audio.playSound(releaseSound, { position: entityProperties.position, volume: VOLUME });
} }
} }
Grabber.prototype.heartBeat = function(entityID) {
var now = Date.now();
if (now - this.lastHeartBeat > HEART_BEAT_INTERVAL) {
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
data["heartBeat"] = now;
setEntityCustomData(GRAB_USER_DATA_KEY, entityID, data);
this.lastHeartBeat = now;
}
};
Grabber.prototype.moveEvent = function(event) { Grabber.prototype.moveEvent = function(event) {
if (!this.isGrabbing) { if (!this.isGrabbing) {
return; return;
} }
mouse.updateDrag(event); mouse.updateDrag(event);
this.heartBeat(this.entityID);
// see if something added/restored gravity // see if something added/restored gravity
var entityProperties = Entities.getEntityProperties(this.entityID); var entityProperties = Entities.getEntityProperties(this.entityID);
if (Vec3.length(entityProperties.gravity) != 0) { if (Vec3.length(entityProperties.gravity) != 0) {

View file

@ -12,7 +12,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
/*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */ /*global print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt, pointInExtents, vec3equal, setEntityCustomData, getEntityCustomData */
Script.include("../libraries/utils.js"); Script.include("/~/system/libraries/utils.js");
// //
@ -28,7 +28,7 @@ var WANT_DEBUG_SEARCH_NAME = null;
var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing var TRIGGER_SMOOTH_RATIO = 0.1; // Time averaging of trigger - 0.0 disables smoothing
var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab var TRIGGER_ON_VALUE = 0.4; // Squeezed just enough to activate search or near grab
var TRIGGER_GRAB_VALUE = 0.85; // Squeezed far enough to complete distant grab var TRIGGER_GRAB_VALUE = 0.75; // Squeezed far enough to complete distant grab
var TRIGGER_OFF_VALUE = 0.15; var TRIGGER_OFF_VALUE = 0.15;
var BUMPER_ON_VALUE = 0.5; var BUMPER_ON_VALUE = 0.5;
@ -102,7 +102,6 @@ var ZERO_VEC = {
}; };
var NULL_UUID = "{00000000-0000-0000-0000-000000000000}"; var NULL_UUID = "{00000000-0000-0000-0000-000000000000}";
var MSEC_PER_SEC = 1000.0;
// these control how long an abandoned pointer line or action will hang around // these control how long an abandoned pointer line or action will hang around
var LIFETIME = 10; var LIFETIME = 10;
@ -740,10 +739,6 @@ function MyController(hand) {
}; };
this.propsArePhysical = function(props) { this.propsArePhysical = function(props) {
if (!props.dynamic && props.parentID != MyAvatar.sessionUUID) {
// if we have parented something, don't do this check on dynamic.
return false;
}
var isPhysical = (props.shapeType && props.shapeType != 'none'); var isPhysical = (props.shapeType && props.shapeType != 'none');
return isPhysical; return isPhysical;
} }
@ -837,7 +832,7 @@ function MyController(hand) {
this.search = function() { this.search = function() {
this.grabbedEntity = null; this.grabbedEntity = null;
this.isInitialGrab = false; this.isInitialGrab = false;
this.doubleParentGrab = false; this.shouldResetParentOnRelease = false;
this.checkForStrayChildren(); this.checkForStrayChildren();
@ -914,7 +909,7 @@ function MyController(hand) {
candidateEntities = rayPickedCandidateEntities.concat(nearPickedCandidateEntities); candidateEntities = rayPickedCandidateEntities.concat(nearPickedCandidateEntities);
var forbiddenNames = ["Grab Debug Entity", "grab pointer"]; var forbiddenNames = ["Grab Debug Entity", "grab pointer"];
var forbiddenTypes = ['Unknown', 'Light', 'ParticleEffect', 'PolyLine', 'Zone']; var forbiddenTypes = ['Unknown', 'Light', 'PolyLine', 'Zone'];
var minDistance = PICK_MAX_DISTANCE; var minDistance = PICK_MAX_DISTANCE;
var i, props, distance, grabbableData; var i, props, distance, grabbableData;
@ -1019,6 +1014,10 @@ function MyController(hand) {
if (this.state == STATE_SEARCHING) { if (this.state == STATE_SEARCHING) {
this.setState(STATE_NEAR_GRABBING); this.setState(STATE_NEAR_GRABBING);
} else { // (this.state == STATE_HOLD_SEARCHING) } else { // (this.state == STATE_HOLD_SEARCHING)
// if there was already an action, we'll need to set the parent back to null once we release
this.shouldResetParentOnRelease = true;
this.previousParentID = props.parentID;
this.previousParentJointIndex = props.parentJointIndex;
this.setState(STATE_HOLD); this.setState(STATE_HOLD);
} }
return; return;
@ -1064,7 +1063,7 @@ function MyController(hand) {
// it's not physical and it's already held via parenting. go ahead and grab it, but // it's not physical and it's already held via parenting. go ahead and grab it, but
// save off the current parent and joint. this wont always be right if there are more than // save off the current parent and joint. this wont always be right if there are more than
// two grabs and the order of release isn't opposite of the order of grabs. // two grabs and the order of release isn't opposite of the order of grabs.
this.doubleParentGrab = true; this.shouldResetParentOnRelease = true;
this.previousParentID = props.parentID; this.previousParentID = props.parentID;
this.previousParentJointIndex = props.parentJointIndex; this.previousParentJointIndex = props.parentJointIndex;
if (this.state == STATE_SEARCHING) { if (this.state == STATE_SEARCHING) {
@ -1149,7 +1148,7 @@ function MyController(hand) {
if (this.actionID === NULL_UUID) { if (this.actionID === NULL_UUID) {
this.actionID = null; this.actionID = null;
} }
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC);
if (this.actionID !== null) { if (this.actionID !== null) {
this.setState(STATE_CONTINUE_DISTANCE_HOLDING); this.setState(STATE_CONTINUE_DISTANCE_HOLDING);
@ -1184,7 +1183,7 @@ function MyController(hand) {
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES); var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
var now = Date.now(); var now = Date.now();
var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds var deltaTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds
this.currentObjectTime = now; this.currentObjectTime = now;
// the action was set up when this.distanceHolding was called. update the targets. // the action was set up when this.distanceHolding was called. update the targets.
@ -1302,7 +1301,7 @@ function MyController(hand) {
ttl: ACTION_TTL ttl: ACTION_TTL
}); });
if (success) { if (success) {
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC);
} else { } else {
print("continueDistanceHolding -- updateAction failed"); print("continueDistanceHolding -- updateAction failed");
} }
@ -1327,7 +1326,7 @@ function MyController(hand) {
return false; return false;
} }
var now = Date.now(); var now = Date.now();
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC);
return true; return true;
}; };
@ -1436,6 +1435,10 @@ function MyController(hand) {
if (!this.setupHoldAction()) { if (!this.setupHoldAction()) {
return; return;
} }
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
action: 'grab',
grabbedEntity: this.grabbedEntity
}));
} else { } else {
// grab entity via parenting // grab entity via parenting
this.actionID = null; this.actionID = null;
@ -1563,7 +1566,7 @@ function MyController(hand) {
var now = Date.now(); var now = Date.now();
var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters
var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds var deltaTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds
if (deltaTime > 0.0) { if (deltaTime > 0.0) {
var worldDeltaPosition = Vec3.subtract(props.position, this.currentObjectPosition); var worldDeltaPosition = Vec3.subtract(props.position, this.currentObjectPosition);
@ -1590,7 +1593,7 @@ function MyController(hand) {
this.callEntityMethodOnGrabbed("continueNearGrab"); this.callEntityMethodOnGrabbed("continueNearGrab");
} }
if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSEC_PER_SEC) { if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSECS_PER_SEC) {
// if less than a 5 seconds left, refresh the actions ttl // if less than a 5 seconds left, refresh the actions ttl
var success = Entities.updateAction(this.grabbedEntity, this.actionID, { var success = Entities.updateAction(this.grabbedEntity, this.actionID, {
hand: this.hand === RIGHT_HAND ? "right" : "left", hand: this.hand === RIGHT_HAND ? "right" : "left",
@ -1603,7 +1606,7 @@ function MyController(hand) {
ignoreIK: this.ignoreIK ignoreIK: this.ignoreIK
}); });
if (success) { if (success) {
this.actionTimeout = now + (ACTION_TTL * MSEC_PER_SEC); this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC);
} else { } else {
print("continueNearGrabbing -- updateAction failed"); print("continueNearGrabbing -- updateAction failed");
Entities.deleteAction(this.grabbedEntity, this.actionID); Entities.deleteAction(this.grabbedEntity, this.actionID);
@ -1838,12 +1841,12 @@ function MyController(hand) {
// things that are held by parenting and dropped with no velocity will end up as "static" in bullet. If // things that are held by parenting and dropped with no velocity will end up as "static" in bullet. If
// it looks like the dropped thing should fall, give it a little velocity. // it looks like the dropped thing should fall, give it a little velocity.
var props = Entities.getEntityProperties(entityID, ["parentID", "velocity"]) var props = Entities.getEntityProperties(entityID, ["parentID", "velocity", "dynamic", "shapeType"])
var parentID = props.parentID; var parentID = props.parentID;
var forceVelocity = false; var forceVelocity = false;
var doSetVelocity = false; var doSetVelocity = false;
if (parentID != NULL_UUID && deactiveProps.parentID == NULL_UUID) { if (parentID != NULL_UUID && deactiveProps.parentID == NULL_UUID && this.propsArePhysical(props)) {
// TODO: EntityScriptingInterface::convertLocationToScriptSemantics should be setting up // TODO: EntityScriptingInterface::convertLocationToScriptSemantics should be setting up
// props.velocity to be a world-frame velocity and localVelocity to be vs parent. Until that // props.velocity to be a world-frame velocity and localVelocity to be vs parent. Until that
// is done, we use a measured velocity here so that things held via a bumper-grab / parenting-grab // is done, we use a measured velocity here so that things held via a bumper-grab / parenting-grab
@ -1881,7 +1884,7 @@ function MyController(hand) {
} }
data = null; data = null;
} else if (this.doubleParentGrab) { } else if (this.shouldResetParentOnRelease) {
// we parent-grabbed this from another parent grab. try to put it back where we found it. // we parent-grabbed this from another parent grab. try to put it back where we found it.
var deactiveProps = { var deactiveProps = {
parentID: this.previousParentID, parentID: this.previousParentID,
@ -1892,7 +1895,8 @@ function MyController(hand) {
Entities.editEntity(entityID, deactiveProps); Entities.editEntity(entityID, deactiveProps);
} else if (noVelocity) { } else if (noVelocity) {
Entities.editEntity(entityID, {velocity: {x: 0.0, y: 0.0, z: 0.0}, Entities.editEntity(entityID, {velocity: {x: 0.0, y: 0.0, z: 0.0},
angularVelocity: {x: 0.0, y: 0.0, z: 0.0}}); angularVelocity: {x: 0.0, y: 0.0, z: 0.0},
dynamic: data["dynamic"]});
} }
} else { } else {
data = null; data = null;