diff --git a/.gitignore b/.gitignore index 4a1c1c227b..cd7fb34eaf 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 24d22fee96..2ef2beb274 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -591,17 +591,17 @@ void EntityTreeRenderer::deleteReleasedModels() { RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, bool precisionPicking, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard) { + const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly) { RayToEntityIntersectionResult result; if (_tree) { EntityTreePointer entityTree = std::static_pointer_cast(_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(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index b1d875c2fb..36e52e6f46 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -149,7 +149,8 @@ private: QList _releasedModels; RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, bool precisionPicking, const QVector& entityIdsToInclude = QVector(), - const QVector& entityIdsToDiscard = QVector()); + const QVector& entityIdsToDiscard = QVector(), bool visibleOnly=false, + bool collidableOnly = false); EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 85a8b17361..306477b10c 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -624,11 +624,11 @@ QVector 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 entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); QVector 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& entityIdsToInclude, const QVector& entityIdsToDiscard) { + Octree::lockType lockType, bool precisionPicking, const QVector& entityIdsToInclude, + const QVector& 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(); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index d5934b1a8d..3a24ff59fd 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -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& entityIdsToInclude, const QVector& entityIdsToDiscard); + bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly = false, bool collidableOnly = false); EntityTreePointer _entityTree; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 89f469037e..ffb6ec31e2 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -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& entityIdsToInclude; + const QVector& 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& entityIdsToInclude; - const QVector& entityIdsToDiscard; - void** intersectedObject; - bool found; - bool precisionPicking; -}; - bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { RayArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast(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& entityIdsToInclude, const QVector& entityIdsToDiscard, void** intersectedObject, - Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { - RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, false, precisionPicking }; + QVector entityIdsToInclude, QVector 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; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 917b9333a5..68c8618482 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -31,6 +31,7 @@ using ModelWeakPointer = std::weak_ptr; 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& entityIdsToInclude = QVector(), - const QVector& entityIdsToDiscard = QVector(), - void** intersectedObject = NULL, - Octree::lockType lockType = Octree::TryLock, - bool* accurateResult = NULL, - bool precisionPicking = false); + QVector entityIdsToInclude, QVector 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; } diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 29274d2e72..629a6b37e4 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -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& entityIdsToInclude, - const QVector& entityIdsToDiscard, void** intersectedObject, bool precisionPicking) { + const QVector& 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& entityIdsToInclude, const QVector& entityIDsToDiscard, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { + const QVector& entityIdsToInclude, const QVector& 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; } diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index d92dfa52dc..e411e8077b 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -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& entityIdsToInclude, - const QVector& entityIdsToDiscard, + const QVector& 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& entityIdsToInclude, - const QVector& entityIdsToDiscard, + const QVector& 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; diff --git a/scripts/system/away.js b/scripts/system/away.js index 716fe1340e..c704da83f4 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -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); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2d3aff44fc..954093854e 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -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); diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index b4a8eefcd2..2a7dbb000b 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -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() {