Merge pull request #8735 from huffman/feat/teleport-improvements

Update teleport.js to ignore invisible or collisionless objects and disallow teleporting to  certain surfaces
This commit is contained in:
Ryan Downe Karpf 2016-10-07 09:56:30 -07:00 committed by GitHub
commit ed2571704f
12 changed files with 130 additions and 72 deletions

2
.gitignore vendored
View file

@ -45,7 +45,7 @@ gvr-interface/libs/*
# ignore files for various dev environments
TAGS
*.swp
*.sw[po]
# ignore node files for the console
node_modules

View file

@ -591,17 +591,17 @@ void EntityTreeRenderer::deleteReleasedModels() {
RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard) {
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
RayToEntityIntersectionResult result;
if (_tree) {
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
OctreeElementPointer element;
EntityItemPointer intersectedEntity = NULL;
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance,
result.face, result.surfaceNormal, entityIdsToInclude, entityIdsToDiscard,
(void**)&intersectedEntity, lockType, &result.accurate,
precisionPicking);
result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction,
entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking,
element, result.distance, result.face, result.surfaceNormal,
(void**)&intersectedEntity, lockType, &result.accurate);
if (result.intersects && intersectedEntity) {
result.entityID = intersectedEntity->getEntityItemID();
result.properties = intersectedEntity->getProperties();

View file

@ -149,7 +149,8 @@ private:
QList<ModelPointer> _releasedModels;
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude = QVector<EntityItemID>(),
const QVector<EntityItemID>& entityIdsToDiscard = QVector<EntityItemID>());
const QVector<EntityItemID>& entityIdsToDiscard = QVector<EntityItemID>(), bool visibleOnly=false,
bool collidableOnly = false);
EntityItemID _currentHoverOverEntityID;
EntityItemID _currentClickingOnEntityID;

View file

@ -624,11 +624,11 @@ QVector<QUuid> EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frust
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking,
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) {
const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
QVector<EntityItemID> entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude);
QVector<EntityItemID> entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard);
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard);
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard, visibleOnly, collidableOnly);
}
// FIXME - we should remove this API and encourage all users to use findRayIntersection() instead. We've changed
@ -643,17 +643,18 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlock
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray,
Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard) {
Octree::lockType lockType, bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) {
RayToEntityIntersectionResult result;
if (_entityTree) {
OctreeElementPointer element;
EntityItemPointer intersectedEntity = NULL;
result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
result.surfaceNormal, entityIdsToInclude, entityIdsToDiscard, (void**)&intersectedEntity, lockType, &result.accurate,
precisionPicking);
result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction,
entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking,
element, result.distance, result.face, result.surfaceNormal,
(void**)&intersectedEntity, lockType, &result.accurate);
if (result.intersects && intersectedEntity) {
result.entityID = intersectedEntity->getEntityItemID();
result.properties = intersectedEntity->getProperties();

View file

@ -143,7 +143,9 @@ public slots:
/// If the scripting context has visible entities, this will determine a ray intersection, the results
/// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate
/// will be false.
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue());
Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false,
const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue(),
bool visibleOnly = false, bool collidableOnly = false);
/// If the scripting context has visible entities, this will determine a ray intersection, and will block in
/// order to return an accurate result
@ -257,7 +259,8 @@ private:
/// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode
RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType,
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard);
bool precisionPicking, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard,
bool visibleOnly = false, bool collidableOnly = false);
EntityTreePointer _entityTree;

View file

@ -29,6 +29,28 @@ static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC *
const float EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME = 60 * 60; // 1 hour
// combines the ray cast arguments into a single object
class RayArgs {
public:
// Inputs
glm::vec3 origin;
glm::vec3 direction;
const QVector<EntityItemID>& entityIdsToInclude;
const QVector<EntityItemID>& entityIdsToDiscard;
bool visibleOnly;
bool collidableOnly;
bool precisionPicking;
// Outputs
OctreeElementPointer& element;
float& distance;
BoxFace& face;
glm::vec3& surfaceNormal;
void** intersectedObject;
bool found;
};
EntityTree::EntityTree(bool shouldReaverage) :
Octree(shouldReaverage),
_fbxService(NULL),
@ -538,40 +560,28 @@ bool EntityTree::findNearPointOperation(OctreeElementPointer element, void* extr
// if this element doesn't contain the point, then none of its children can contain the point, so stop searching
return false;
}
// combines the ray cast arguments into a single object
class RayArgs {
public:
glm::vec3 origin;
glm::vec3 direction;
OctreeElementPointer& element;
float& distance;
BoxFace& face;
glm::vec3& surfaceNormal;
const QVector<EntityItemID>& entityIdsToInclude;
const QVector<EntityItemID>& entityIdsToDiscard;
void** intersectedObject;
bool found;
bool precisionPicking;
};
bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) {
RayArgs* args = static_cast<RayArgs*>(extraData);
bool keepSearching = true;
EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast<EntityTreeElement>(element);
if (entityTreeElementPointer ->findRayIntersection(args->origin, args->direction, keepSearching,
if (entityTreeElementPointer->findRayIntersection(args->origin, args->direction, keepSearching,
args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude,
args->entityIdsToDiscard, args->intersectedObject, args->precisionPicking)) {
args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->intersectedObject, args->precisionPicking)) {
args->found = true;
}
return keepSearching;
}
bool EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIdsToDiscard, void** intersectedObject,
Octree::lockType lockType, bool* accurateResult, bool precisionPicking) {
RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, false, precisionPicking };
QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,
bool visibleOnly, bool collidableOnly, bool precisionPicking,
OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject,
Octree::lockType lockType, bool* accurateResult) {
RayArgs args = { origin, direction, entityIdsToInclude, entityIdsToDiscard,
visibleOnly, collidableOnly, precisionPicking,
element, distance, face, surfaceNormal, intersectedObject, false };
distance = FLT_MAX;
bool requireLock = lockType == Octree::Lock;

View file

@ -31,6 +31,7 @@ using ModelWeakPointer = std::weak_ptr<Model>;
class EntitySimulation;
class NewlyCreatedEntityHook {
public:
virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) = 0;
@ -89,13 +90,11 @@ public:
const SharedNodePointer& senderNode) override;
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
const QVector<EntityItemID>& entityIdsToInclude = QVector<EntityItemID>(),
const QVector<EntityItemID>& entityIdsToDiscard = QVector<EntityItemID>(),
void** intersectedObject = NULL,
Octree::lockType lockType = Octree::TryLock,
bool* accurateResult = NULL,
bool precisionPicking = false);
QVector<EntityItemID> entityIdsToInclude, QVector<EntityItemID> entityIdsToDiscard,
bool visibleOnly, bool collidableOnly, bool precisionPicking,
OctreeElementPointer& node, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, void** intersectedObject = NULL,
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
virtual bool rootElementHasData() const override { return true; }

View file

@ -534,7 +534,8 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3
bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard, void** intersectedObject, bool precisionPicking) {
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly,
void** intersectedObject, bool precisionPicking) {
keepSearching = true; // assume that we will continue searching after this.
@ -559,7 +560,8 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::
if (_cube.contains(origin) || distanceToElementCube < distance) {
if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails,
face, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, precisionPicking, distanceToElementCube)) {
face, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly,
intersectedObject, precisionPicking, distanceToElementCube)) {
if (distanceToElementDetails < distance) {
distance = distanceToElementDetails;
@ -574,13 +576,16 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::
bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching,
OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal,
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIDsToDiscard, void** intersectedObject, bool precisionPicking, float distanceToElementCube) {
const QVector<EntityItemID>& entityIdsToInclude, const QVector<EntityItemID>& entityIDsToDiscard,
bool visibleOnly, bool collidableOnly, void** intersectedObject, bool precisionPicking, float distanceToElementCube) {
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
int entityNumber = 0;
bool somethingIntersected = false;
forEachEntity([&](EntityItemPointer entity) {
if ( (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) || (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) {
if ( (visibleOnly && !entity->isVisible()) || (collidableOnly && (entity->getCollisionless() || entity->getShapeType() == SHAPE_TYPE_NONE))
|| (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID()))
|| (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) {
return;
}
@ -638,7 +643,7 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
if (localDistance < distance && entity->getType() != EntityTypes::ParticleEffect) {
distance = localDistance;
face = localFace;
surfaceNormal = localSurfaceNormal;
surfaceNormal = glm::vec3(rotation * glm::vec4(localSurfaceNormal, 1.0f));
*intersectedObject = (void*)entity.get();
somethingIntersected = true;
}

View file

@ -147,12 +147,12 @@ public:
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& node, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard,
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly = false, bool collidableOnly = false,
void** intersectedObject = NULL, bool precisionPicking = false);
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal, const QVector<EntityItemID>& entityIdsToInclude,
const QVector<EntityItemID>& entityIdsToDiscard,
const QVector<EntityItemID>& entityIdsToDiscard, bool visibleOnly, bool collidableOnly,
void** intersectedObject, bool precisionPicking, float distanceToElementCube);
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const override;

View file

@ -142,6 +142,7 @@ function ifAvatarMovedGoActive() {
}
// MAIN CONTROL
var isEnabled = true;
var wasMuted, isAway;
var wasOverlaysVisible = Menu.isOptionChecked("Overlays");
var eventMappingName = "io.highfidelity.away"; // goActive on hand controller button events, too.
@ -159,7 +160,7 @@ function safeGetHMDMounted() {
var wasHmdMounted = safeGetHMDMounted();
function goAway() {
if (isAway) {
if (!isEnabled || isAway) {
return;
}
@ -274,6 +275,24 @@ function maybeGoAway() {
}
}
function setEnabled(value) {
print("setting away enabled: ", value);
if (!value) {
goActive();
}
isEnabled = value;
}
var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable";
var handleMessage = function(channel, message, sender) {
print("Got away message");
if (channel == CHANNEL_AWAY_ENABLE) {
setEnabled(message == 'enable');
}
}
Messages.subscribe(CHANNEL_AWAY_ENABLE);
Messages.messageReceived.connect(handleMessage);
Script.update.connect(maybeMoveOverlay);
Script.update.connect(maybeGoAway);

View file

@ -344,7 +344,7 @@ function entityHasActions(entityID) {
}
function findRayIntersection(pickRay, precise, include, exclude) {
var entities = Entities.findRayIntersection(pickRay, precise, include, exclude);
var entities = Entities.findRayIntersection(pickRay, precise, include, exclude, true);
var overlays = Overlays.findRayIntersection(pickRay);
if (!overlays.intersects || (entities.intersects && (entities.distance <= overlays.distance))) {
return entities;
@ -1158,9 +1158,9 @@ function MyController(hand) {
var intersection;
if (USE_BLACKLIST === true && blacklist.length !== 0) {
intersection = findRayIntersection(pickRay, true, [], blacklist);
intersection = findRayIntersection(pickRay, true, [], blacklist, true);
} else {
intersection = findRayIntersection(pickRay, true);
intersection = findRayIntersection(pickRay, true, [], [], true);
}
if (intersection.intersects) {
@ -1608,7 +1608,6 @@ function MyController(hand) {
};
this.distanceHoldingEnter = function() {
Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'both');
this.clearEquipHaptics();
this.grabPointSphereOff();
@ -1872,12 +1871,6 @@ function MyController(hand) {
};
this.nearGrabbingEnter = function() {
if (this.hand === 0) {
Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'left');
}
if (this.hand === 1) {
Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'right');
}
this.grabPointSphereOff();
this.lineOff();
this.overlayLineOff();
@ -2215,7 +2208,7 @@ function MyController(hand) {
var now = Date.now();
if (now - this.lastPickTime > MSECS_PER_SEC / PICKS_PER_SECOND_PER_HAND) {
var intersection = findRayIntersection(pickRay, true);
var intersection = findRayIntersection(pickRay, true, [], [], true);
if (intersection.accurate || intersection.overlayID) {
this.lastPickTime = now;
if (intersection.entityID != this.grabbedEntity) {
@ -2343,7 +2336,6 @@ function MyController(hand) {
};
this.release = function() {
Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none');
this.turnOffVisualizations();
var noVelocity = false;
@ -2714,6 +2706,20 @@ var handleHandMessages = function(channel, message, sender) {
}
handToDisable = message;
}
} else if (channel === 'Hifi-Grab-Disable') {
data = JSON.parse(message);
if (data.holdEnabled !== undefined) {
print("holdEnabled: ", data.holdEnabled);
holdEnabled = data.holdEnabled;
}
if (data.nearGrabEnabled !== undefined) {
print("nearGrabEnabled: ", data.nearGrabEnabled);
nearGrabEnabled = data.nearGrabEnabled;
}
if (data.farGrabEnabled !== undefined) {
print("farGrabEnabled: ", data.farGrabEnabled);
farGrabEnabled = data.farGrabEnabled;
}
} else if (channel === 'Hifi-Hand-Grab') {
try {
data = JSON.parse(message);

View file

@ -279,7 +279,7 @@ function Teleporter() {
var location = Vec3.sum(rightPickRay.origin, Vec3.multiply(rightPickRay.direction, 50));
var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity]);
var rightIntersection = Entities.findRayIntersection(teleporter.rightPickRay, true, [], [this.targetEntity], true, true);
if (rightIntersection.intersects) {
if (this.tooClose === true) {
@ -342,7 +342,7 @@ function Teleporter() {
var location = Vec3.sum(MyAvatar.position, Vec3.multiply(leftPickRay.direction, 50));
var leftIntersection = Entities.findRayIntersection(teleporter.leftPickRay, true, [], [this.targetEntity]);
var leftIntersection = Entities.findRayIntersection(teleporter.leftPickRay, true, [], [this.targetEntity], true, true);
if (leftIntersection.intersects) {
@ -459,7 +459,7 @@ function Teleporter() {
z: intersection.intersection.z
};
this.tooClose = isTooCloseToTeleport(position);
this.tooClose = isValidTeleportLocation(position, intersection.surfaceNormal);
var towardUs = Quat.fromPitchYawRollDegrees(0, euler.y, 0);
Overlays.editOverlay(this.targetOverlay, {
@ -480,7 +480,7 @@ function Teleporter() {
z: intersection.intersection.z
};
this.tooClose = isTooCloseToTeleport(position);
this.tooClose = isValidTeleportLocation(position, intersection.surfaceNormal);
var towardUs = Quat.fromPitchYawRollDegrees(0, euler.y, 0);
Overlays.editOverlay(this.cancelOverlay, {
@ -509,7 +509,12 @@ function Teleporter() {
var offset = getAvatarFootOffset();
this.intersection.intersection.y += offset;
this.exitTeleportMode();
this.smoothArrival();
// Disable smooth arrival, possibly temporarily
//this.smoothArrival();
MyAvatar.position = _this.intersection.intersection;
_this.deleteTargetOverlay();
_this.deleteCancelOverlay();
HMD.centerUI();
}
};
@ -627,8 +632,17 @@ function isMoving() {
}
};
function isTooCloseToTeleport(position) {
return Vec3.distance(MyAvatar.position, position) <= TELEPORT_CANCEL_RANGE;
// When determininig whether you can teleport to a location, the normal of the
// point that is being intersected with is looked at. If this normal is more
// than MAX_ANGLE_FROM_UP_TO_TELEPORT degrees from <0, 1, 0> (straight up), then
// you can't teleport there.
var MAX_ANGLE_FROM_UP_TO_TELEPORT = 70;
function isValidTeleportLocation(position, surfaceNormal) {
var adj = Math.sqrt(surfaceNormal.x * surfaceNormal.x + surfaceNormal.z * surfaceNormal.z);
var angleUp = Math.atan2(surfaceNormal.y, adj) * (180 / Math.PI);
return angleUp < (90 - MAX_ANGLE_FROM_UP_TO_TELEPORT) ||
angleUp > (90 + MAX_ANGLE_FROM_UP_TO_TELEPORT) ||
Vec3.distance(MyAvatar.position, position) <= TELEPORT_CANCEL_RANGE;
};
function registerMappings() {