Merge remote-tracking branch 'upstream/master'

This commit is contained in:
James B. Pollack 2015-10-05 14:10:25 -07:00
commit bec7bef8df
6 changed files with 94 additions and 53 deletions

View file

@ -87,15 +87,18 @@ function getTag() {
} }
function entityIsGrabbedByOther(entityID) { function entityIsGrabbedByOther(entityID) {
// by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*.
var actionIDs = Entities.getActionIDs(entityID); var actionIDs = Entities.getActionIDs(entityID);
for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
var actionID = actionIDs[actionIndex]; var actionID = actionIDs[actionIndex];
var actionArguments = Entities.getActionArguments(entityID, actionID); var actionArguments = Entities.getActionArguments(entityID, actionID);
var tag = actionArguments["tag"]; var tag = actionArguments["tag"];
if (tag == getTag()) { if (tag == getTag()) {
// we see a grab-*uuid* shaped tag, but it's our tag, so that's okay.
continue; continue;
} }
if (tag.slice(0, 5) == "grab-") { if (tag.slice(0, 5) == "grab-") {
// we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it.
return true; return true;
} }
} }
@ -164,6 +167,12 @@ function MyController(hand, triggerAction) {
} }
}; };
this.setState = function(newState) {
// print("STATE: " + this.state + " --> " + newState);
this.state = newState;
}
this.lineOn = function(closePoint, farPoint, color) { this.lineOn = function(closePoint, farPoint, color) {
// draw a line // draw a line
if (this.pointer === null) { if (this.pointer === null) {
@ -217,14 +226,14 @@ function MyController(hand, triggerAction) {
this.off = function() { this.off = function() {
if (this.triggerSmoothedSqueezed()) { if (this.triggerSmoothedSqueezed()) {
this.state = STATE_SEARCHING; this.setState(STATE_SEARCHING);
return; return;
} }
} }
this.search = function() { this.search = function() {
if (this.triggerSmoothedReleased()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.setState(STATE_RELEASE);
return; return;
} }
@ -235,6 +244,8 @@ function MyController(hand, triggerAction) {
direction: Quat.getUp(this.getHandRotation()) direction: Quat.getUp(this.getHandRotation())
}; };
this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
var defaultGrabbableData = { var defaultGrabbableData = {
grabbable: true grabbable: true
}; };
@ -250,20 +261,20 @@ function MyController(hand, triggerAction) {
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, defaultGrabbableData); var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, intersection.entityID, defaultGrabbableData);
if (grabbableData.grabbable === false) { if (grabbableData.grabbable === false) {
this.grabbedEntity = null;
return; return;
} }
if (intersectionDistance < NEAR_PICK_MAX_DISTANCE) { if (intersectionDistance < NEAR_PICK_MAX_DISTANCE) {
// the hand is very close to the intersected object. go into close-grabbing mode. // the hand is very close to the intersected object. go into close-grabbing mode.
this.state = STATE_NEAR_GRABBING; this.setState(STATE_NEAR_GRABBING);
} else { } else {
// don't allow two people to distance grab the same object
if (entityIsGrabbedByOther(intersection.entityID)) { if (entityIsGrabbedByOther(intersection.entityID)) {
// don't allow two people to distance grab the same object this.grabbedEntity = null;
return; } else {
// the hand is far from the intersected object. go into distance-holding mode
this.setState(STATE_DISTANCE_HOLDING);
} }
// 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);
} }
} else { } else {
// forward ray test failed, try sphere test. // forward ray test failed, try sphere test.
@ -287,15 +298,14 @@ function MyController(hand, triggerAction) {
} }
} }
if (this.grabbedEntity === null) { if (this.grabbedEntity === null) {
this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR); // this.lineOn(pickRay.origin, Vec3.multiply(pickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
} else if (props.locked === 0 && props.collisionsWillMove === 1) { } else if (props.locked === 0 && props.collisionsWillMove === 1) {
this.state = STATE_NEAR_GRABBING; this.setState(STATE_NEAR_GRABBING);
} else if (props.collisionsWillMove === 0) { } 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 // We have grabbed a non-physical object, so we want to trigger a non-colliding event as opposed to a grab event
this.state = STATE_NEAR_GRABBING_NON_COLLIDING; this.setState(STATE_NEAR_GRABBING_NON_COLLIDING);
} }
} }
}; };
this.distanceHolding = function() { this.distanceHolding = function() {
@ -325,7 +335,7 @@ function MyController(hand, triggerAction) {
} }
if (this.actionID !== null) { if (this.actionID !== null) {
this.state = STATE_CONTINUE_DISTANCE_HOLDING; this.setState(STATE_CONTINUE_DISTANCE_HOLDING);
this.activateEntity(this.grabbedEntity); this.activateEntity(this.grabbedEntity);
if (this.hand === RIGHT_HAND) { if (this.hand === RIGHT_HAND) {
Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
@ -342,7 +352,7 @@ function MyController(hand, triggerAction) {
this.continueDistanceHolding = function() { this.continueDistanceHolding = function() {
if (this.triggerSmoothedReleased()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.setState(STATE_RELEASE);
return; return;
} }
@ -400,7 +410,7 @@ function MyController(hand, triggerAction) {
var now = Date.now(); var now = Date.now();
var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds
this.computeReleaseVelocity(deltaPosition, deltaTime, false); this.computeReleaseVelocity(deltaPosition, deltaTime, false);
this.currentObjectPosition = newObjectPosition; this.currentObjectPosition = newObjectPosition;
this.currentObjectTime = now; this.currentObjectTime = now;
@ -423,7 +433,7 @@ function MyController(hand, triggerAction) {
this.nearGrabbing = function() { this.nearGrabbing = function() {
if (this.triggerSmoothedReleased()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.setState(STATE_RELEASE);
return; return;
} }
@ -454,7 +464,7 @@ function MyController(hand, triggerAction) {
if (this.actionID === NULL_ACTION_ID) { if (this.actionID === NULL_ACTION_ID) {
this.actionID = null; this.actionID = null;
} else { } else {
this.state = STATE_CONTINUE_NEAR_GRABBING; this.setState(STATE_CONTINUE_NEAR_GRABBING);
if (this.hand === RIGHT_HAND) { if (this.hand === RIGHT_HAND) {
Entities.callEntityMethod(this.grabbedEntity, "setRightHand"); Entities.callEntityMethod(this.grabbedEntity, "setRightHand");
} else { } else {
@ -471,7 +481,7 @@ function MyController(hand, triggerAction) {
this.continueNearGrabbing = function() { this.continueNearGrabbing = function() {
if (this.triggerSmoothedReleased()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.setState(STATE_RELEASE);
return; return;
} }
@ -498,7 +508,7 @@ function MyController(hand, triggerAction) {
this.nearGrabbingNonColliding = function() { this.nearGrabbingNonColliding = function() {
if (this.triggerSmoothedReleased()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.setState(STATE_RELEASE);
return; return;
} }
if (this.hand === RIGHT_HAND) { if (this.hand === RIGHT_HAND) {
@ -507,12 +517,12 @@ function MyController(hand, triggerAction) {
Entities.callEntityMethod(this.grabbedEntity, "setLeftHand"); Entities.callEntityMethod(this.grabbedEntity, "setLeftHand");
} }
Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding"); Entities.callEntityMethod(this.grabbedEntity, "startNearGrabNonColliding");
this.state = STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING; this.setState(STATE_CONTINUE_NEAR_GRABBING_NON_COLLIDING);
}; };
this.continueNearGrabbingNonColliding = function() { this.continueNearGrabbingNonColliding = function() {
if (this.triggerSmoothedReleased()) { if (this.triggerSmoothedReleased()) {
this.state = STATE_RELEASE; this.setState(STATE_RELEASE);
return; return;
} }
Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding"); Entities.callEntityMethod(this.grabbedEntity, "continueNearGrabbingNonColliding");
@ -623,7 +633,7 @@ function MyController(hand, triggerAction) {
this.grabbedVelocity = ZERO_VEC; this.grabbedVelocity = ZERO_VEC;
this.grabbedEntity = null; this.grabbedEntity = null;
this.actionID = null; this.actionID = null;
this.state = STATE_OFF; this.setState(STATE_OFF);
}; };
this.cleanup = function() { this.cleanup = function() {

View file

@ -45,26 +45,25 @@ var IDENTITY_QUAT = {
z: 0, z: 0,
w: 0 w: 0
}; };
var ACTION_LIFETIME = 120; // 2 minutes var ACTION_LIFETIME = 10; // seconds
function getTag() { function getTag() {
return "grab-" + MyAvatar.sessionUUID; return "grab-" + MyAvatar.sessionUUID;
} }
function entityIsGrabbedByOther(entityID) { function entityIsGrabbedByOther(entityID) {
// by convention, a distance grab sets the tag of its action to be grab-*owner-session-id*.
var actionIDs = Entities.getActionIDs(entityID); var actionIDs = Entities.getActionIDs(entityID);
var actionIndex; for (var actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) {
var actionID; var actionID = actionIDs[actionIndex];
var actionArguments; var actionArguments = Entities.getActionArguments(entityID, actionID);
var tag; var tag = actionArguments["tag"];
for (actionIndex = 0; actionIndex < actionIDs.length; actionIndex++) { if (tag == getTag()) {
actionID = actionIDs[actionIndex]; // we see a grab-*uuid* shaped tag, but it's our tag, so that's okay.
actionArguments = Entities.getActionArguments(entityID, actionID);
tag = actionArguments.tag;
if (tag === getTag()) {
continue; continue;
} }
if (tag.slice(0, 5) === "grab-") { if (tag.slice(0, 5) == "grab-") {
// we see a grab-*uuid* shaped tag and it's not ours, so someone else is grabbing it.
return true; return true;
} }
} }
@ -530,4 +529,4 @@ Controller.mousePressEvent.connect(pressEvent);
Controller.mouseMoveEvent.connect(moveEvent); Controller.mouseMoveEvent.connect(moveEvent);
Controller.mouseReleaseEvent.connect(releaseEvent); Controller.mouseReleaseEvent.connect(releaseEvent);
Controller.keyPressEvent.connect(keyPressEvent); Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent);

View file

@ -65,9 +65,9 @@ EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEnt
if (action) { if (action) {
action->deserialize(data); action->deserialize(data);
} if (action->lifetimeIsOver()) {
if (action->lifetimeIsOver()) { return nullptr;
return nullptr; }
} }
return action; return action;

View file

@ -48,6 +48,8 @@ public:
virtual bool lifetimeIsOver() { return false; } virtual bool lifetimeIsOver() { return false; }
bool locallyAddedButNotYetReceived = false;
protected: protected:
virtual glm::vec3 getPosition() = 0; virtual glm::vec3 getPosition() = 0;
virtual void setPosition(glm::vec3 position) = 0; virtual void setPosition(glm::vec3 position) = 0;

View file

@ -34,6 +34,7 @@
bool EntityItem::_sendPhysicsUpdates = true; bool EntityItem::_sendPhysicsUpdates = true;
int EntityItem::_maxActionsDataSize = 800; int EntityItem::_maxActionsDataSize = 800;
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
EntityItem::EntityItem(const EntityItemID& entityItemID) : EntityItem::EntityItem(const EntityItemID& entityItemID) :
_type(EntityTypes::Unknown), _type(EntityTypes::Unknown),
@ -1505,6 +1506,8 @@ bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer act
result = addActionInternal(simulation, action); result = addActionInternal(simulation, action);
if (!result) { if (!result) {
removeActionInternal(action->getID()); removeActionInternal(action->getID());
} else {
action->locallyAddedButNotYetReceived = true;
} }
}); });
@ -1525,7 +1528,8 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi
simulation->addAction(action); simulation->addAction(action);
bool success; bool success;
QByteArray newDataCache = serializeActions(success); QByteArray newDataCache;
serializeActions(success, newDataCache);
if (success) { if (success) {
_allActionsDataCache = newDataCache; _allActionsDataCache = newDataCache;
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
@ -1546,7 +1550,7 @@ bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionI
success = action->updateArguments(arguments); success = action->updateArguments(arguments);
if (success) { if (success) {
_allActionsDataCache = serializeActions(success); serializeActions(success, _allActionsDataCache);
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
} else { } else {
qDebug() << "EntityItem::updateAction failed"; qDebug() << "EntityItem::updateAction failed";
@ -1566,6 +1570,7 @@ bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionI
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* simulation) { bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* simulation) {
assertWriteLocked(); assertWriteLocked();
_previouslyDeletedActions.insert(actionID, usecTimestampNow());
if (_objectActions.contains(actionID)) { if (_objectActions.contains(actionID)) {
if (!simulation) { if (!simulation) {
EntityTreePointer entityTree = _element ? _element->getTree() : nullptr; EntityTreePointer entityTree = _element ? _element->getTree() : nullptr;
@ -1581,7 +1586,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
} }
bool success = true; bool success = true;
_allActionsDataCache = serializeActions(success); serializeActions(success, _allActionsDataCache);
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION; _dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
return success; return success;
} }
@ -1618,6 +1623,8 @@ void EntityItem::deserializeActions() {
void EntityItem::deserializeActionsInternal() { void EntityItem::deserializeActionsInternal() {
assertWriteLocked(); assertWriteLocked();
quint64 now = usecTimestampNow();
if (!_element) { if (!_element) {
return; return;
} }
@ -1643,6 +1650,10 @@ void EntityItem::deserializeActionsInternal() {
QUuid actionID; QUuid actionID;
serializedActionStream >> actionType; serializedActionStream >> actionType;
serializedActionStream >> actionID; serializedActionStream >> actionID;
if (_previouslyDeletedActions.contains(actionID)) {
continue;
}
updated << actionID; updated << actionID;
if (_objectActions.contains(actionID)) { if (_objectActions.contains(actionID)) {
@ -1650,14 +1661,14 @@ void EntityItem::deserializeActionsInternal() {
// TODO: make sure types match? there isn't currently a way to // TODO: make sure types match? there isn't currently a way to
// change the type of an existing action. // change the type of an existing action.
action->deserialize(serializedAction); action->deserialize(serializedAction);
action->locallyAddedButNotYetReceived = false;
} else { } else {
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>(); auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
// EntityItemPointer entity = entityTree->findEntityByEntityItemID(_id, false);
EntityItemPointer entity = shared_from_this(); EntityItemPointer entity = shared_from_this();
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;
} }
} }
} }
@ -1667,11 +1678,27 @@ void EntityItem::deserializeActionsInternal() {
while (i != _objectActions.end()) { while (i != _objectActions.end()) {
QUuid id = i.key(); QUuid id = i.key();
if (!updated.contains(id)) { if (!updated.contains(id)) {
_actionsToRemove << id; 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) {
_actionsToRemove << id;
_previouslyDeletedActions.insert(id, now);
}
} }
i++; i++;
} }
// trim down _previouslyDeletedActions
QMutableHashIterator<QUuid, quint64> _previouslyDeletedIter(_previouslyDeletedActions);
while (_previouslyDeletedIter.hasNext()) {
_previouslyDeletedIter.next();
if (now - _previouslyDeletedIter.value() > _rememberDeletedActionTime) {
_previouslyDeletedActions.remove(_previouslyDeletedIter.key());
}
}
_actionDataDirty = true;
return; return;
} }
@ -1697,13 +1724,13 @@ void EntityItem::setActionDataInternal(QByteArray actionData) {
deserializeActionsInternal(); deserializeActionsInternal();
} }
QByteArray EntityItem::serializeActions(bool& success) const { void EntityItem::serializeActions(bool& success, QByteArray& result) const {
assertLocked(); assertLocked();
QByteArray result;
if (_objectActions.size() == 0) { if (_objectActions.size() == 0) {
success = true; success = true;
return QByteArray(); result.clear();
return;
} }
QVector<QByteArray> serializedActions; QVector<QByteArray> serializedActions;
@ -1721,21 +1748,20 @@ QByteArray EntityItem::serializeActions(bool& success) const {
if (result.size() >= _maxActionsDataSize) { if (result.size() >= _maxActionsDataSize) {
success = false; success = false;
return result; return;
} }
success = true; success = true;
return result; return;
} }
const QByteArray EntityItem::getActionDataInternal() const { const QByteArray EntityItem::getActionDataInternal() const {
if (_actionDataDirty) { if (_actionDataDirty) {
bool success; bool success;
QByteArray newDataCache = serializeActions(success); serializeActions(success, _allActionsDataCache);
if (success) { if (success) {
_allActionsDataCache = newDataCache; _actionDataDirty = false;
} }
_actionDataDirty = false;
} }
return _allActionsDataCache; return _allActionsDataCache;
} }

View file

@ -508,17 +508,21 @@ protected:
bool addActionInternal(EntitySimulation* simulation, EntityActionPointer action); bool addActionInternal(EntitySimulation* simulation, EntityActionPointer action);
bool removeActionInternal(const QUuid& actionID, EntitySimulation* simulation = nullptr); bool removeActionInternal(const QUuid& actionID, EntitySimulation* simulation = nullptr);
void deserializeActionsInternal(); void deserializeActionsInternal();
QByteArray serializeActions(bool& success) const; void serializeActions(bool& success, QByteArray& result) const;
QHash<QUuid, EntityActionPointer> _objectActions; QHash<QUuid, EntityActionPointer> _objectActions;
static int _maxActionsDataSize; static int _maxActionsDataSize;
mutable QByteArray _allActionsDataCache; mutable QByteArray _allActionsDataCache;
// when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is // when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is
// ready. This means we can't find our EntityItemPointer or add the action to the simulation. These // ready. This means we can't find our EntityItemPointer or add the action to the simulation. These
// are used to keep track of and work around this situation. // are used to keep track of and work around this situation.
void checkWaitingToRemove(EntitySimulation* simulation = nullptr); void checkWaitingToRemove(EntitySimulation* simulation = nullptr);
mutable QSet<QUuid> _actionsToRemove; mutable QSet<QUuid> _actionsToRemove;
mutable bool _actionDataDirty = false; mutable bool _actionDataDirty = false;
// _previouslyDeletedActions is used to avoid an action being re-added due to server round-trip lag
static quint64 _rememberDeletedActionTime;
mutable QHash<QUuid, quint64> _previouslyDeletedActions;
}; };
#endif // hifi_EntityItem_h #endif // hifi_EntityItem_h