mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 11:07:07 +02:00
Merge pull request #5292 from sethalves/palm-rotation
action changes to allow "hold" action to track palm rotation
This commit is contained in:
commit
270ea7af9e
14 changed files with 347 additions and 115 deletions
|
@ -17,8 +17,7 @@ EntityActionPointer assignmentActionFactory(EntityActionType type, const QUuid&
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EntityActionPointer AssignmentActionFactory::factory(EntitySimulation* simulation,
|
EntityActionPointer AssignmentActionFactory::factory(EntityActionType type,
|
||||||
EntityActionType type,
|
|
||||||
const QUuid& id,
|
const QUuid& id,
|
||||||
EntityItemPointer ownerEntity,
|
EntityItemPointer ownerEntity,
|
||||||
QVariantMap arguments) {
|
QVariantMap arguments) {
|
||||||
|
@ -33,9 +32,7 @@ EntityActionPointer AssignmentActionFactory::factory(EntitySimulation* simulatio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EntityActionPointer AssignmentActionFactory::factoryBA(EntitySimulation* simulation,
|
EntityActionPointer AssignmentActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
|
||||||
EntityItemPointer ownerEntity,
|
|
||||||
QByteArray data) {
|
|
||||||
QDataStream serializedActionDataStream(data);
|
QDataStream serializedActionDataStream(data);
|
||||||
EntityActionType type;
|
EntityActionType type;
|
||||||
QUuid id;
|
QUuid id;
|
||||||
|
|
|
@ -19,14 +19,11 @@ class AssignmentActionFactory : public EntityActionFactoryInterface {
|
||||||
public:
|
public:
|
||||||
AssignmentActionFactory() : EntityActionFactoryInterface() { }
|
AssignmentActionFactory() : EntityActionFactoryInterface() { }
|
||||||
virtual ~AssignmentActionFactory() { }
|
virtual ~AssignmentActionFactory() { }
|
||||||
virtual EntityActionPointer factory(EntitySimulation* simulation,
|
virtual EntityActionPointer factory(EntityActionType type,
|
||||||
EntityActionType type,
|
|
||||||
const QUuid& id,
|
const QUuid& id,
|
||||||
EntityItemPointer ownerEntity,
|
EntityItemPointer ownerEntity,
|
||||||
QVariantMap arguments);
|
QVariantMap arguments);
|
||||||
virtual EntityActionPointer factoryBA(EntitySimulation* simulation,
|
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity, QByteArray data);
|
||||||
EntityItemPointer ownerEntity,
|
|
||||||
QByteArray data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AssignmentActionFactory_h
|
#endif // hifi_AssignmentActionFactory_h
|
||||||
|
|
73
examples/stick-hydra.js
Normal file
73
examples/stick-hydra.js
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// stick-hydra.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Seth Alves on 2015-7-9
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Allow avatar to hold a stick and control it with a hand-tracker
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
var hand = "left";
|
||||||
|
var nullActionID = "00000000-0000-0000-0000-000000000000";
|
||||||
|
var controllerID;
|
||||||
|
var controllerActive;
|
||||||
|
var stickID = null;
|
||||||
|
var actionID = nullActionID;
|
||||||
|
var makingNewStick = false;
|
||||||
|
|
||||||
|
function makeNewStick() {
|
||||||
|
if (makingNewStick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
makingNewStick = true;
|
||||||
|
cleanUp();
|
||||||
|
// sometimes if this is run immediately the stick doesn't get created? use a timer.
|
||||||
|
Script.setTimeout(function() {
|
||||||
|
stickID = Entities.addEntity({
|
||||||
|
type: "Model",
|
||||||
|
name: "stick",
|
||||||
|
modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
|
||||||
|
compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
|
||||||
|
dimensions: {x: .11, y: .11, z: 1.0},
|
||||||
|
position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
|
||||||
|
rotation: MyAvatar.orientation,
|
||||||
|
damping: .1,
|
||||||
|
collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav",
|
||||||
|
restitution: 0.01,
|
||||||
|
collisionsWillMove: true
|
||||||
|
});
|
||||||
|
actionID = Entities.addAction("hold", stickID,
|
||||||
|
{relativePosition: {x: 0.0, y: 0.0, z: -0.5},
|
||||||
|
relativeRotation: Quat.fromVec3Degrees({x: 0.0, y: 90.0, z: 0.0}),
|
||||||
|
hand: hand,
|
||||||
|
timeScale: 0.15});
|
||||||
|
if (actionID == nullActionID) {
|
||||||
|
cleanUp();
|
||||||
|
}
|
||||||
|
makingNewStick = false;
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function cleanUp() {
|
||||||
|
if (stickID) {
|
||||||
|
Entities.deleteEntity(stickID);
|
||||||
|
stickID = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initControls(){
|
||||||
|
if (hand == "right") {
|
||||||
|
controllerID = 3; // right handed
|
||||||
|
} else {
|
||||||
|
controllerID = 4; // left handed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(cleanUp);
|
||||||
|
makeNewStick();
|
|
@ -35,8 +35,7 @@ EntityActionPointer interfaceActionFactory(EntityActionType type, const QUuid& i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation,
|
EntityActionPointer InterfaceActionFactory::factory(EntityActionType type,
|
||||||
EntityActionType type,
|
|
||||||
const QUuid& id,
|
const QUuid& id,
|
||||||
EntityItemPointer ownerEntity,
|
EntityItemPointer ownerEntity,
|
||||||
QVariantMap arguments) {
|
QVariantMap arguments) {
|
||||||
|
@ -51,9 +50,7 @@ EntityActionPointer InterfaceActionFactory::factory(EntitySimulation* simulation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
EntityActionPointer InterfaceActionFactory::factoryBA(EntitySimulation* simulation,
|
EntityActionPointer InterfaceActionFactory::factoryBA(EntityItemPointer ownerEntity, QByteArray data) {
|
||||||
EntityItemPointer ownerEntity,
|
|
||||||
QByteArray data) {
|
|
||||||
QDataStream serializedArgumentStream(data);
|
QDataStream serializedArgumentStream(data);
|
||||||
EntityActionType type;
|
EntityActionType type;
|
||||||
QUuid id;
|
QUuid id;
|
||||||
|
|
|
@ -18,13 +18,11 @@ class InterfaceActionFactory : public EntityActionFactoryInterface {
|
||||||
public:
|
public:
|
||||||
InterfaceActionFactory() : EntityActionFactoryInterface() { }
|
InterfaceActionFactory() : EntityActionFactoryInterface() { }
|
||||||
virtual ~InterfaceActionFactory() { }
|
virtual ~InterfaceActionFactory() { }
|
||||||
virtual EntityActionPointer factory(EntitySimulation* simulation,
|
virtual EntityActionPointer factory(EntityActionType type,
|
||||||
EntityActionType type,
|
|
||||||
const QUuid& id,
|
const QUuid& id,
|
||||||
EntityItemPointer ownerEntity,
|
EntityItemPointer ownerEntity,
|
||||||
QVariantMap arguments);
|
QVariantMap arguments);
|
||||||
virtual EntityActionPointer factoryBA(EntitySimulation* simulation,
|
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
|
||||||
EntityItemPointer ownerEntity,
|
|
||||||
QByteArray data);
|
QByteArray data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -52,16 +52,18 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 palmPosition;
|
glm::vec3 palmPosition;
|
||||||
|
glm::quat palmRotation;
|
||||||
if (_hand == "right") {
|
if (_hand == "right") {
|
||||||
palmPosition = myAvatar->getRightPalmPosition();
|
palmPosition = myAvatar->getRightPalmPosition();
|
||||||
|
palmRotation = myAvatar->getRightPalmRotation();
|
||||||
} else {
|
} else {
|
||||||
palmPosition = myAvatar->getLeftPalmPosition();
|
palmPosition = myAvatar->getLeftPalmPosition();
|
||||||
|
palmRotation = myAvatar->getLeftPalmRotation();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rotation = myAvatar->getWorldAlignedOrientation();
|
auto rotation = palmRotation * _relativeRotation;
|
||||||
auto offset = rotation * _relativePosition;
|
auto offset = rotation * _relativePosition;
|
||||||
auto position = palmPosition + offset;
|
auto position = palmPosition + offset;
|
||||||
rotation *= _relativeRotation;
|
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
if (!tryLockForWrite()) {
|
if (!tryLockForWrite()) {
|
||||||
|
@ -83,6 +85,13 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_positionalTarget != position || _rotationalTarget != rotation) {
|
||||||
|
auto ownerEntity = _ownerEntity.lock();
|
||||||
|
if (ownerEntity) {
|
||||||
|
ownerEntity->setActionDataDirty(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_positionalTarget = position;
|
_positionalTarget = position;
|
||||||
_rotationalTarget = rotation;
|
_rotationalTarget = rotation;
|
||||||
unlock();
|
unlock();
|
||||||
|
|
|
@ -371,6 +371,12 @@ glm::vec3 MyAvatar::getLeftPalmPosition() {
|
||||||
return leftHandPosition;
|
return leftHandPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::quat MyAvatar::getLeftPalmRotation() {
|
||||||
|
glm::quat leftRotation;
|
||||||
|
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
|
||||||
|
return leftRotation;
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec3 MyAvatar::getRightPalmPosition() {
|
glm::vec3 MyAvatar::getRightPalmPosition() {
|
||||||
glm::vec3 rightHandPosition;
|
glm::vec3 rightHandPosition;
|
||||||
getSkeletonModel().getRightHandPosition(rightHandPosition);
|
getSkeletonModel().getRightHandPosition(rightHandPosition);
|
||||||
|
@ -380,6 +386,12 @@ glm::vec3 MyAvatar::getRightPalmPosition() {
|
||||||
return rightHandPosition;
|
return rightHandPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::quat MyAvatar::getRightPalmRotation() {
|
||||||
|
glm::quat rightRotation;
|
||||||
|
getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
|
||||||
|
return rightRotation;
|
||||||
|
}
|
||||||
|
|
||||||
void MyAvatar::clearReferential() {
|
void MyAvatar::clearReferential() {
|
||||||
changeReferential(NULL);
|
changeReferential(NULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,10 +195,12 @@ public slots:
|
||||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||||
|
|
||||||
void updateMotionBehavior();
|
void updateMotionBehavior();
|
||||||
|
|
||||||
glm::vec3 getLeftPalmPosition();
|
glm::vec3 getLeftPalmPosition();
|
||||||
|
glm::quat getLeftPalmRotation();
|
||||||
glm::vec3 getRightPalmPosition();
|
glm::vec3 getRightPalmPosition();
|
||||||
|
glm::quat getRightPalmRotation();
|
||||||
|
|
||||||
void clearReferential();
|
void clearReferential();
|
||||||
bool setModelReferential(const QUuid& id);
|
bool setModelReferential(const QUuid& id);
|
||||||
bool setJointReferential(const QUuid& id, int jointIndex);
|
bool setJointReferential(const QUuid& id, int jointIndex);
|
||||||
|
|
|
@ -23,13 +23,11 @@ class EntityActionFactoryInterface : public QObject, public Dependency {
|
||||||
public:
|
public:
|
||||||
EntityActionFactoryInterface() { }
|
EntityActionFactoryInterface() { }
|
||||||
virtual ~EntityActionFactoryInterface() { }
|
virtual ~EntityActionFactoryInterface() { }
|
||||||
virtual EntityActionPointer factory(EntitySimulation* simulation,
|
virtual EntityActionPointer factory(EntityActionType type,
|
||||||
EntityActionType type,
|
|
||||||
const QUuid& id,
|
const QUuid& id,
|
||||||
EntityItemPointer ownerEntity,
|
EntityItemPointer ownerEntity,
|
||||||
QVariantMap arguments) { assert(false); return nullptr; }
|
QVariantMap arguments) { assert(false); return nullptr; }
|
||||||
virtual EntityActionPointer factoryBA(EntitySimulation* simulation,
|
virtual EntityActionPointer factoryBA(EntityItemPointer ownerEntity,
|
||||||
EntityItemPointer ownerEntity,
|
|
||||||
QByteArray data) { assert(false); return nullptr; }
|
QByteArray data) { assert(false); return nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1489,20 +1489,22 @@ void EntityItem::clearSimulationOwnership() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) {
|
bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) {
|
||||||
|
lockForWrite();
|
||||||
checkWaitingToRemove(simulation);
|
checkWaitingToRemove(simulation);
|
||||||
if (!checkWaitingActionData(simulation)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = addActionInternal(simulation, action);
|
bool result = addActionInternal(simulation, action);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
removeAction(simulation, action->getID());
|
removeAction(simulation, action->getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlock();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPointer action) {
|
bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPointer action) {
|
||||||
|
assertLocked();
|
||||||
assert(action);
|
assert(action);
|
||||||
assert(simulation);
|
assert(simulation);
|
||||||
auto actionOwnerEntity = action->getOwnerEntity().lock();
|
auto actionOwnerEntity = action->getOwnerEntity().lock();
|
||||||
|
@ -1523,36 +1525,37 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments) {
|
bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments) {
|
||||||
|
lockForWrite();
|
||||||
checkWaitingToRemove(simulation);
|
checkWaitingToRemove(simulation);
|
||||||
if (!checkWaitingActionData(simulation)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_objectActions.contains(actionID)) {
|
if (!_objectActions.contains(actionID)) {
|
||||||
|
unlock();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
EntityActionPointer action = _objectActions[actionID];
|
EntityActionPointer action = _objectActions[actionID];
|
||||||
bool success = action->updateArguments(arguments);
|
|
||||||
|
|
||||||
|
bool success = action->updateArguments(arguments);
|
||||||
if (success) {
|
if (success) {
|
||||||
_allActionsDataCache = serializeActions(success);
|
_allActionsDataCache = serializeActions(success);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "EntityItem::updateAction failed";
|
qDebug() << "EntityItem::updateAction failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlock();
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionID) {
|
bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionID) {
|
||||||
|
lockForWrite();
|
||||||
checkWaitingToRemove(simulation);
|
checkWaitingToRemove(simulation);
|
||||||
if (!checkWaitingActionData(simulation)) {
|
|
||||||
return false;;
|
|
||||||
}
|
|
||||||
|
|
||||||
return removeActionInternal(actionID);
|
bool success = removeActionInternal(actionID);
|
||||||
|
unlock();
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* simulation) {
|
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* simulation) {
|
||||||
|
assertWriteLocked();
|
||||||
if (_objectActions.contains(actionID)) {
|
if (_objectActions.contains(actionID)) {
|
||||||
if (!simulation) {
|
if (!simulation) {
|
||||||
EntityTree* entityTree = _element ? _element->getTree() : nullptr;
|
EntityTree* entityTree = _element ? _element->getTree() : nullptr;
|
||||||
|
@ -1575,7 +1578,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityItem::clearActions(EntitySimulation* simulation) {
|
bool EntityItem::clearActions(EntitySimulation* simulation) {
|
||||||
_waitingActionData.clear();
|
lockForWrite();
|
||||||
QHash<QUuid, EntityActionPointer>::iterator i = _objectActions.begin();
|
QHash<QUuid, EntityActionPointer>::iterator i = _objectActions.begin();
|
||||||
while (i != _objectActions.end()) {
|
while (i != _objectActions.end()) {
|
||||||
const QUuid id = i.key();
|
const QUuid id = i.key();
|
||||||
|
@ -1584,85 +1587,84 @@ bool EntityItem::clearActions(EntitySimulation* simulation) {
|
||||||
action->setOwnerEntity(nullptr);
|
action->setOwnerEntity(nullptr);
|
||||||
action->removeFromSimulation(simulation);
|
action->removeFromSimulation(simulation);
|
||||||
}
|
}
|
||||||
|
// empty _serializedActions means no actions for the EntityItem
|
||||||
_actionsToRemove.clear();
|
_actionsToRemove.clear();
|
||||||
_allActionsDataCache.clear();
|
_allActionsDataCache.clear();
|
||||||
|
unlock();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityItem::deserializeActions(QByteArray allActionsData, EntitySimulation* simulation) const {
|
|
||||||
bool success = true;
|
void EntityItem::deserializeActions() {
|
||||||
QVector<QByteArray> serializedActions;
|
assertUnlocked();
|
||||||
if (allActionsData.size() > 0) {
|
lockForWrite();
|
||||||
QDataStream serializedActionsStream(allActionsData);
|
deserializeActionsInternal();
|
||||||
serializedActionsStream >> serializedActions;
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EntityItem::deserializeActionsInternal() {
|
||||||
|
assertWriteLocked();
|
||||||
|
|
||||||
|
if (!_element) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of which actions got added or updated by the new actionData
|
// Keep track of which actions got added or updated by the new actionData
|
||||||
QSet<QUuid> updated;
|
|
||||||
|
|
||||||
EntityTree* entityTree = _element ? _element->getTree() : nullptr;
|
EntityTree* entityTree = _element ? _element->getTree() : nullptr;
|
||||||
if (!simulation) {
|
assert(entityTree);
|
||||||
simulation = entityTree ? entityTree->getSimulation() : nullptr;
|
EntitySimulation* simulation = entityTree ? entityTree->getSimulation() : nullptr;
|
||||||
|
assert(simulation);
|
||||||
|
|
||||||
|
QVector<QByteArray> serializedActions;
|
||||||
|
if (_allActionsDataCache.size() > 0) {
|
||||||
|
QDataStream serializedActionsStream(_allActionsDataCache);
|
||||||
|
serializedActionsStream >> serializedActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (simulation && entityTree) {
|
QSet<QUuid> updated;
|
||||||
foreach(QByteArray serializedAction, serializedActions) {
|
|
||||||
QDataStream serializedActionStream(serializedAction);
|
|
||||||
EntityActionType actionType;
|
|
||||||
QUuid actionID;
|
|
||||||
serializedActionStream >> actionType;
|
|
||||||
serializedActionStream >> actionID;
|
|
||||||
updated << actionID;
|
|
||||||
|
|
||||||
if (_objectActions.contains(actionID)) {
|
foreach(QByteArray serializedAction, serializedActions) {
|
||||||
EntityActionPointer action = _objectActions[actionID];
|
QDataStream serializedActionStream(serializedAction);
|
||||||
// TODO: make sure types match? there isn't currently a way to
|
EntityActionType actionType;
|
||||||
// change the type of an existing action.
|
QUuid actionID;
|
||||||
action->deserialize(serializedAction);
|
serializedActionStream >> actionType;
|
||||||
} else {
|
serializedActionStream >> actionID;
|
||||||
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
updated << actionID;
|
||||||
if (simulation) {
|
|
||||||
EntityItemPointer entity = entityTree->findEntityByEntityItemID(_id);
|
if (_objectActions.contains(actionID)) {
|
||||||
EntityActionPointer action = actionFactory->factoryBA(simulation, entity, serializedAction);
|
EntityActionPointer action = _objectActions[actionID];
|
||||||
if (action) {
|
// TODO: make sure types match? there isn't currently a way to
|
||||||
entity->addActionInternal(simulation, action);
|
// change the type of an existing action.
|
||||||
}
|
action->deserialize(serializedAction);
|
||||||
} else {
|
} else {
|
||||||
// we can't yet add the action. This method will be called later.
|
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
|
||||||
success = false;
|
|
||||||
}
|
// EntityItemPointer entity = entityTree->findEntityByEntityItemID(_id, false);
|
||||||
|
EntityItemPointer entity = shared_from_this();
|
||||||
|
EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction);
|
||||||
|
if (action) {
|
||||||
|
entity->addActionInternal(simulation, action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove any actions that weren't included in the new data.
|
// remove any actions that weren't included in the new data.
|
||||||
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
|
||||||
while (i != _objectActions.end()) {
|
while (i != _objectActions.end()) {
|
||||||
const QUuid id = i.key();
|
QUuid id = i.key();
|
||||||
if (!updated.contains(id)) {
|
if (!updated.contains(id)) {
|
||||||
_actionsToRemove << id;
|
_actionsToRemove << id;
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
} else {
|
i++;
|
||||||
// no simulation
|
|
||||||
success = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
bool EntityItem::checkWaitingActionData(EntitySimulation* simulation) const {
|
|
||||||
if (_waitingActionData.size() == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool success = deserializeActions(_waitingActionData, simulation);
|
|
||||||
if (success) {
|
|
||||||
_waitingActionData.clear();
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::checkWaitingToRemove(EntitySimulation* simulation) {
|
void EntityItem::checkWaitingToRemove(EntitySimulation* simulation) {
|
||||||
|
assertLocked();
|
||||||
foreach(QUuid actionID, _actionsToRemove) {
|
foreach(QUuid actionID, _actionsToRemove) {
|
||||||
removeActionInternal(actionID, simulation);
|
removeActionInternal(actionID, simulation);
|
||||||
}
|
}
|
||||||
|
@ -1670,21 +1672,22 @@ void EntityItem::checkWaitingToRemove(EntitySimulation* simulation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::setActionData(QByteArray actionData) {
|
void EntityItem::setActionData(QByteArray actionData) {
|
||||||
|
assertUnlocked();
|
||||||
|
lockForWrite();
|
||||||
|
setActionDataInternal(actionData);
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::setActionDataInternal(QByteArray actionData) {
|
||||||
|
assertWriteLocked();
|
||||||
checkWaitingToRemove();
|
checkWaitingToRemove();
|
||||||
bool success = deserializeActions(actionData);
|
|
||||||
_allActionsDataCache = actionData;
|
_allActionsDataCache = actionData;
|
||||||
if (success) {
|
deserializeActionsInternal();
|
||||||
_waitingActionData.clear();
|
|
||||||
} else {
|
|
||||||
_waitingActionData = actionData;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray EntityItem::serializeActions(bool& success) const {
|
QByteArray EntityItem::serializeActions(bool& success) const {
|
||||||
|
assertLocked();
|
||||||
QByteArray result;
|
QByteArray result;
|
||||||
if (!checkWaitingActionData()) {
|
|
||||||
return _waitingActionData;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_objectActions.size() == 0) {
|
if (_objectActions.size() == 0) {
|
||||||
success = true;
|
success = true;
|
||||||
|
@ -1713,21 +1716,132 @@ QByteArray EntityItem::serializeActions(bool& success) const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QByteArray EntityItem::getActionData() const {
|
const QByteArray EntityItem::getActionDataInternal() const {
|
||||||
|
if (_actionDataDirty) {
|
||||||
|
bool success;
|
||||||
|
QByteArray newDataCache = serializeActions(success);
|
||||||
|
if (success) {
|
||||||
|
_allActionsDataCache = newDataCache;
|
||||||
|
}
|
||||||
|
_actionDataDirty = false;
|
||||||
|
}
|
||||||
return _allActionsDataCache;
|
return _allActionsDataCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QByteArray EntityItem::getActionData() const {
|
||||||
|
assertUnlocked();
|
||||||
|
lockForRead();
|
||||||
|
auto result = getActionDataInternal();
|
||||||
|
unlock();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
|
QVariantMap EntityItem::getActionArguments(const QUuid& actionID) const {
|
||||||
QVariantMap result;
|
QVariantMap result;
|
||||||
|
lockForRead();
|
||||||
if (!checkWaitingActionData()) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_objectActions.contains(actionID)) {
|
if (_objectActions.contains(actionID)) {
|
||||||
EntityActionPointer action = _objectActions[actionID];
|
EntityActionPointer action = _objectActions[actionID];
|
||||||
result = action->getArguments();
|
result = action->getArguments();
|
||||||
result["type"] = EntityActionInterface::actionTypeToString(action->getType());
|
result["type"] = EntityActionInterface::actionTypeToString(action->getType());
|
||||||
}
|
}
|
||||||
|
unlock();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define ENABLE_LOCKING 1
|
||||||
|
|
||||||
|
#ifdef ENABLE_LOCKING
|
||||||
|
void EntityItem::lockForRead() const {
|
||||||
|
_lock.lockForRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityItem::tryLockForRead() const {
|
||||||
|
return _lock.tryLockForRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::lockForWrite() const {
|
||||||
|
_lock.lockForWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityItem::tryLockForWrite() const {
|
||||||
|
return _lock.tryLockForWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::unlock() const {
|
||||||
|
_lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityItem::isLocked() const {
|
||||||
|
bool readSuccess = tryLockForRead();
|
||||||
|
if (readSuccess) {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
bool writeSuccess = tryLockForWrite();
|
||||||
|
if (writeSuccess) {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
if (readSuccess && writeSuccess) {
|
||||||
|
return false; // if we can take both kinds of lock, there was no previous lock
|
||||||
|
}
|
||||||
|
return true; // either read or write failed, so there is some lock in place.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool EntityItem::isWriteLocked() const {
|
||||||
|
bool readSuccess = tryLockForRead();
|
||||||
|
if (readSuccess) {
|
||||||
|
unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool writeSuccess = tryLockForWrite();
|
||||||
|
if (writeSuccess) {
|
||||||
|
unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true; // either read or write failed, so there is some lock in place.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool EntityItem::isUnlocked() const {
|
||||||
|
// this can't be sure -- this may get unlucky and hit locks from other threads. what we're actually trying
|
||||||
|
// to discover is if *this* thread hasn't locked the EntityItem. Try repeatedly to take both kinds of lock.
|
||||||
|
bool readSuccess = false;
|
||||||
|
for (int i=0; i<80; i++) {
|
||||||
|
readSuccess = tryLockForRead();
|
||||||
|
if (readSuccess) {
|
||||||
|
unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QThread::usleep(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool writeSuccess = false;
|
||||||
|
if (readSuccess) {
|
||||||
|
for (int i=0; i<80; i++) {
|
||||||
|
writeSuccess = tryLockForWrite();
|
||||||
|
if (writeSuccess) {
|
||||||
|
unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QThread::usleep(300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readSuccess && writeSuccess) {
|
||||||
|
return true; // if we can take both kinds of lock, there was no previous lock
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void EntityItem::lockForRead() const { }
|
||||||
|
bool EntityItem::tryLockForRead() const { return true; }
|
||||||
|
void EntityItem::lockForWrite() const { }
|
||||||
|
bool EntityItem::tryLockForWrite() const { return true; }
|
||||||
|
void EntityItem::unlock() const { }
|
||||||
|
bool EntityItem::isLocked() const { return true; }
|
||||||
|
bool EntityItem::isWriteLocked() const { return true; }
|
||||||
|
bool EntityItem::isUnlocked() const { return true; }
|
||||||
|
#endif
|
||||||
|
|
|
@ -68,10 +68,28 @@ const float ACTIVATION_ANGULAR_VELOCITY_DELTA = 0.03f;
|
||||||
#define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10))
|
#define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10))
|
||||||
#define debugTreeVector(V) V << "[" << V << " in meters ]"
|
#define debugTreeVector(V) V << "[" << V << " in meters ]"
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
#define assertLocked() assert(isLocked())
|
||||||
|
#else
|
||||||
|
#define assertLocked()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
#define assertWriteLocked() assert(isWriteLocked())
|
||||||
|
#else
|
||||||
|
#define assertWriteLocked()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
#define assertUnlocked() assert(isUnlocked())
|
||||||
|
#else
|
||||||
|
#define assertUnlocked()
|
||||||
|
#endif
|
||||||
|
|
||||||
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
|
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
|
||||||
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
|
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
|
||||||
/// one directly, instead you must only construct one of it's derived classes with additional features.
|
/// one directly, instead you must only construct one of it's derived classes with additional features.
|
||||||
class EntityItem {
|
class EntityItem : public std::enable_shared_from_this<EntityItem> {
|
||||||
// These two classes manage lists of EntityItem pointers and must be able to cleanup pointers when an EntityItem is deleted.
|
// These two classes manage lists of EntityItem pointers and must be able to cleanup pointers when an EntityItem is deleted.
|
||||||
// To make the cleanup robust each EntityItem has backpointers to its manager classes (which are only ever set/cleared by
|
// To make the cleanup robust each EntityItem has backpointers to its manager classes (which are only ever set/cleared by
|
||||||
// the managers themselves, hence they are fiends) whose NULL status can be used to determine which managers still need to
|
// the managers themselves, hence they are fiends) whose NULL status can be used to determine which managers still need to
|
||||||
|
@ -395,9 +413,14 @@ public:
|
||||||
bool hasActions() { return !_objectActions.empty(); }
|
bool hasActions() { return !_objectActions.empty(); }
|
||||||
QList<QUuid> getActionIDs() { return _objectActions.keys(); }
|
QList<QUuid> getActionIDs() { return _objectActions.keys(); }
|
||||||
QVariantMap getActionArguments(const QUuid& actionID) const;
|
QVariantMap getActionArguments(const QUuid& actionID) const;
|
||||||
|
void deserializeActions();
|
||||||
|
void setActionDataDirty(bool value) const { _actionDataDirty = value; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
const QByteArray getActionDataInternal() const;
|
||||||
|
void setActionDataInternal(QByteArray actionData);
|
||||||
|
|
||||||
static bool _sendPhysicsUpdates;
|
static bool _sendPhysicsUpdates;
|
||||||
EntityTypes::EntityType _type;
|
EntityTypes::EntityType _type;
|
||||||
QUuid _id;
|
QUuid _id;
|
||||||
|
@ -470,18 +493,28 @@ 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);
|
||||||
bool deserializeActions(QByteArray allActionsData, EntitySimulation* simulation = nullptr) const;
|
void deserializeActionsInternal();
|
||||||
QByteArray serializeActions(bool& success) const;
|
QByteArray serializeActions(bool& success) 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.
|
||||||
bool checkWaitingActionData(EntitySimulation* simulation = nullptr) const;
|
|
||||||
void checkWaitingToRemove(EntitySimulation* simulation = nullptr);
|
void checkWaitingToRemove(EntitySimulation* simulation = nullptr);
|
||||||
mutable QByteArray _waitingActionData;
|
|
||||||
mutable QSet<QUuid> _actionsToRemove;
|
mutable QSet<QUuid> _actionsToRemove;
|
||||||
|
mutable bool _actionDataDirty = false;
|
||||||
|
|
||||||
|
mutable QReadWriteLock _lock;
|
||||||
|
void lockForRead() const;
|
||||||
|
bool tryLockForRead() const;
|
||||||
|
void lockForWrite() const;
|
||||||
|
bool tryLockForWrite() const;
|
||||||
|
void unlock() const;
|
||||||
|
bool isLocked() const;
|
||||||
|
bool isWriteLocked() const;
|
||||||
|
bool isUnlocked() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityItem_h
|
#endif // hifi_EntityItem_h
|
||||||
|
|
|
@ -574,7 +574,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
||||||
if (actionType == ACTION_TYPE_NONE) {
|
if (actionType == ACTION_TYPE_NONE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
EntityActionPointer action = actionFactory->factory(simulation, actionType, actionID, entity, arguments);
|
EntityActionPointer action = actionFactory->factory(actionType, actionID, entity, arguments);
|
||||||
if (action) {
|
if (action) {
|
||||||
entity->addAction(simulation, action);
|
entity->addAction(simulation, action);
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
|
@ -146,6 +146,7 @@ void EntitySimulation::sortEntitiesThatMoved() {
|
||||||
|
|
||||||
void EntitySimulation::addEntity(EntityItemPointer entity) {
|
void EntitySimulation::addEntity(EntityItemPointer entity) {
|
||||||
assert(entity);
|
assert(entity);
|
||||||
|
entity->deserializeActions();
|
||||||
if (entity->isMortal()) {
|
if (entity->isMortal()) {
|
||||||
_mortalEntities.insert(entity);
|
_mortalEntities.insert(entity);
|
||||||
quint64 expiry = entity->getExpiry();
|
quint64 expiry = entity->getExpiry();
|
||||||
|
|
|
@ -287,6 +287,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_serverActionData != _entity->getActionData()) {
|
if (_serverActionData != _entity->getActionData()) {
|
||||||
|
setOutgoingPriority(SCRIPT_EDIT_SIMULATION_PRIORITY);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue