diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index 40530b7701..fab512f787 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -618,6 +618,8 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic
             result.intersects = true;
             result.avatarID = avatar->getID();
             result.distance = distance;
+            result.face = face;
+            result.surfaceNormal = surfaceNormal;
             result.extraInfo = extraInfo;
         }
     }
@@ -688,6 +690,8 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector
             result.intersects = true;
             result.avatarID = avatar->getID();
             result.parabolicDistance = parabolicDistance;
+            result.face = face;
+            result.surfaceNormal = surfaceNormal;
             result.extraInfo = extraInfo;
         }
     }
diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp
index 3d1b1c410d..be3fa57b0c 100644
--- a/interface/src/raypick/ParabolaPick.cpp
+++ b/interface/src/raypick/ParabolaPick.cpp
@@ -39,7 +39,7 @@ PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick)
 PickResultPointer ParabolaPick::getAvatarIntersection(const PickParabola& pick) {
     ParabolaToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findParabolaIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
     if (avatarRes.intersects) {
-        return std::make_shared<ParabolaPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.parabolicDistance, avatarRes.intersection, pick, glm::vec3(NAN), avatarRes.extraInfo);
+        return std::make_shared<ParabolaPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.parabolicDistance, avatarRes.intersection, pick, avatarRes.surfaceNormal, avatarRes.extraInfo);
     } else {
         return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
     }
diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp
index 42612ef46d..6ae516bfa4 100644
--- a/interface/src/raypick/ParabolaPointer.cpp
+++ b/interface/src/raypick/ParabolaPointer.cpp
@@ -156,7 +156,6 @@ void ParabolaPointer::RenderState::editParabola(const glm::vec3& color, float al
             item.setIsVisibleInSecondaryCamera(isVisibleInSecondaryCamera);
             item.setEnabled(enabled);
             item.updateKey();
-            item.updateUniformBuffer();
         });
         scene->enqueueTransaction(transaction);
     }
@@ -182,7 +181,6 @@ void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::ve
             item.setAcceleration(acceleration);
             item.setParabolicDistance(parabolicDistance);
             item.setWidth(width);
-            item.updateUniformBuffer();
         });
         scene->enqueueTransaction(transaction);
     }
@@ -293,7 +291,6 @@ ParabolaPointer::RenderState::ParabolaRenderItem::ParabolaRenderItem(const glm::
     setAlpha(alpha);
     setWidth(width);
     updateKey();
-    updateUniformBuffer();
 }
 
 void ParabolaPointer::RenderState::ParabolaRenderItem::setVisible(bool visible) {
@@ -302,12 +299,16 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::setVisible(bool visible)
     } else {
         _key = render::ItemKey::Builder(_key).withInvisible();
     }
+    _visible = visible;
 }
 
 void ParabolaPointer::RenderState::ParabolaRenderItem::updateKey() {
-    auto builder = _parabolaData.color.a < 1.0f ? render::ItemKey::Builder::transparentShape() : render::ItemKey::Builder::opaqueShape();
+    // FIXME: There's no way to designate a render item as non-shadow-reciever, and since a parabola's bounding box covers the entire domain,
+    // it seems to block all shadows.  I think this is a bug with shadows.
+    //auto builder = _parabolaData.color.a < 1.0f ? render::ItemKey::Builder::transparentShape() : render::ItemKey::Builder::opaqueShape();
+    auto builder = render::ItemKey::Builder::transparentShape();
 
-    if (_enabled) {
+    if (_enabled && _visible) {
         builder.withVisible();
     } else {
         builder.withInvisible();
@@ -358,6 +359,10 @@ const gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::get
 }
 
 void ParabolaPointer::RenderState::ParabolaRenderItem::render(RenderArgs* args) {
+    if (!_visible) {
+        return;
+    }
+
     gpu::Batch& batch = *(args->_batch);
 
     Transform transform;
@@ -366,12 +371,17 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::render(RenderArgs* args)
 
     batch.setPipeline(getParabolaPipeline());
 
+    const int MAX_SECTIONS = 100;
+    if (glm::length2(_parabolaData.acceleration) < EPSILON) {
+        _parabolaData.numSections = 1;
+    } else {
+        _parabolaData.numSections = glm::clamp((int)(_parabolaData.parabolicDistance + 1) * 10, 1, MAX_SECTIONS);
+    }
+    updateUniformBuffer();
     batch.setUniformBuffer(0, _uniformBuffer);
 
-    // TODO: variable number of sections, depending on ? (acceleration?, parabolicDistance?)
-    const int NUM_SECTIONS = 25; // must match value in parabola.slv
     // We draw 2 * n + 2 vertices for a triangle strip
-    batch.draw(gpu::TRIANGLE_STRIP, 2 * NUM_SECTIONS + 2, 0);
+    batch.draw(gpu::TRIANGLE_STRIP, 2 * _parabolaData.numSections + 2, 0);
 }
 
 namespace render {
diff --git a/interface/src/raypick/ParabolaPointer.h b/interface/src/raypick/ParabolaPointer.h
index 4a4a27e8fc..28e34523c2 100644
--- a/interface/src/raypick/ParabolaPointer.h
+++ b/interface/src/raypick/ParabolaPointer.h
@@ -57,6 +57,7 @@ public:
 
             glm::vec3 _origin { 0.0f };
             bool _isVisibleInSecondaryCamera { DEFAULT_PARABOLA_ISVISIBLEINSECONDARYCAMERA };
+            bool _visible { false };
             bool _enabled { false };
 
             struct ParabolaData {
@@ -65,6 +66,8 @@ public:
                 vec3 acceleration { 0.0f };
                 float width { DEFAULT_PARABOLA_WIDTH };
                 vec4 color { vec4(DEFAULT_PARABOLA_COLOR)};
+                int numSections { 0 };
+                ivec3 spare;
             };
 
             ParabolaData _parabolaData;
diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp
index 664e124ea0..13ec063b80 100644
--- a/interface/src/raypick/PathPointer.cpp
+++ b/interface/src/raypick/PathPointer.cpp
@@ -113,13 +113,12 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick
             const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f);
             endVec = pos + rot * (dim * (DEFAULT_REGISTRATION_POINT - registrationPoint));
             glm::vec3 direction = endVec - origin;
-            float distance = glm::distance(origin, endVec);
+            distance = glm::distance(origin, endVec);
             glm::vec3 normalizedDirection = glm::normalize(direction);
 
             type = _lockEndObject.isOverlay ? IntersectionType::OVERLAY : IntersectionType::ENTITY;
             id = _lockEndObject.id;
             intersection = endVec;
-            distance = distance;
             surfaceNormal = -normalizedDirection;
             setVisualPickResultInternal(visualPickResult, type, id, intersection, distance, surfaceNormal);
         } else if (type != IntersectionType::NONE && _lockEnd) {
@@ -134,7 +133,6 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick
             glm::vec3 direction = endVec - origin;
             distance = glm::distance(origin, endVec);
             glm::vec3 normalizedDirection = glm::normalize(direction);
-            type = type;
             intersection = endVec;
             surfaceNormal = -normalizedDirection;
             setVisualPickResultInternal(visualPickResult, type, id, intersection, distance, surfaceNormal);
diff --git a/interface/src/raypick/PathPointer.h b/interface/src/raypick/PathPointer.h
index 1e85ad7a92..a8b3d1bcdb 100644
--- a/interface/src/raypick/PathPointer.h
+++ b/interface/src/raypick/PathPointer.h
@@ -79,11 +79,11 @@ public:
     void updateVisuals(const PickResultPointer& prevRayPickResult) override;
 
 protected:
-    PointerTriggers _triggers;
-    float _pathLength { 0.0f };
     RenderStateMap _renderStates;
     DefaultRenderStateMap _defaultRenderStates;
     std::string _currentRenderState { "" };
+    PointerTriggers _triggers;
+    float _pathLength { 0.0f };
     bool _faceAvatar;
     bool _followNormal;
     bool _centerEndY;
diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp
index 75b5e77fd8..96b41dcc72 100644
--- a/interface/src/raypick/RayPick.cpp
+++ b/interface/src/raypick/RayPick.cpp
@@ -39,7 +39,7 @@ PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) {
 PickResultPointer RayPick::getAvatarIntersection(const PickRay& pick) {
     RayToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findRayIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
     if (avatarRes.intersects) {
-        return std::make_shared<RayPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, pick, glm::vec3(NAN), avatarRes.extraInfo);
+        return std::make_shared<RayPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, pick, avatarRes.surfaceNormal, avatarRes.extraInfo);
     } else {
         return std::make_shared<RayPickResult>(pick.toVariantMap());
     }
diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp
index 9a054ffaa3..de4ff94719 100644
--- a/interface/src/ui/overlays/Overlays.cpp
+++ b/interface/src/ui/overlays/Overlays.cpp
@@ -622,75 +622,37 @@ ParabolaToOverlayIntersectionResult Overlays::findParabolaIntersectionVector(con
 }
 
 QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, const RayToOverlayIntersectionResult& value) {
-    auto obj = engine->newObject();
+    QScriptValue obj = engine->newObject();
     obj.setProperty("intersects", value.intersects);
-    obj.setProperty("overlayID", OverlayIDtoScriptValue(engine, value.overlayID));
+    QScriptValue overlayIDValue = quuidToScriptValue(engine, value.overlayID);
+    obj.setProperty("overlayID", overlayIDValue);
     obj.setProperty("distance", value.distance);
+    obj.setProperty("face", boxFaceToString(value.face));
 
-    QString faceName = "";
-    // handle BoxFace
-    switch (value.face) {
-        case MIN_X_FACE:
-            faceName = "MIN_X_FACE";
-            break;
-        case MAX_X_FACE:
-            faceName = "MAX_X_FACE";
-            break;
-        case MIN_Y_FACE:
-            faceName = "MIN_Y_FACE";
-            break;
-        case MAX_Y_FACE:
-            faceName = "MAX_Y_FACE";
-            break;
-        case MIN_Z_FACE:
-            faceName = "MIN_Z_FACE";
-            break;
-        case MAX_Z_FACE:
-            faceName = "MAX_Z_FACE";
-            break;
-        default:
-        case UNKNOWN_FACE:
-            faceName = "UNKNOWN_FACE";
-            break;
-    }
-    obj.setProperty("face", faceName);
-    auto intersection = vec3toScriptValue(engine, value.intersection);
+    QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
     obj.setProperty("intersection", intersection);
+    QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal);
+    obj.setProperty("surfaceNormal", surfaceNormal);
     obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo));
     return obj;
 }
 
-void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& objectVar, RayToOverlayIntersectionResult& value) {
-    QVariantMap object = objectVar.toVariant().toMap();
-    value.intersects = object["intersects"].toBool();
-    value.overlayID = OverlayID(QUuid(object["overlayID"].toString()));
-    value.distance = object["distance"].toFloat();
+void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, RayToOverlayIntersectionResult& value) {
+    value.intersects = object.property("intersects").toVariant().toBool();
+    QScriptValue overlayIDValue = object.property("overlayID");
+    quuidFromScriptValue(overlayIDValue, value.overlayID);
+    value.distance = object.property("distance").toVariant().toFloat();
+    value.face = boxFaceFromString(object.property("face").toVariant().toString());
 
-    QString faceName = object["face"].toString();
-    if (faceName == "MIN_X_FACE") {
-        value.face = MIN_X_FACE;
-    } else if (faceName == "MAX_X_FACE") {
-        value.face = MAX_X_FACE;
-    } else if (faceName == "MIN_Y_FACE") {
-        value.face = MIN_Y_FACE;
-    } else if (faceName == "MAX_Y_FACE") {
-        value.face = MAX_Y_FACE;
-    } else if (faceName == "MIN_Z_FACE") {
-        value.face = MIN_Z_FACE;
-    } else if (faceName == "MAX_Z_FACE") {
-        value.face = MAX_Z_FACE;
-    } else {
-        value.face = UNKNOWN_FACE;
-    };
-    auto intersection = object["intersection"];
+    QScriptValue intersection = object.property("intersection");
     if (intersection.isValid()) {
-        bool valid;
-        auto newIntersection = vec3FromVariant(intersection, valid);
-        if (valid) {
-            value.intersection = newIntersection;
-        }
+        vec3FromScriptValue(intersection, value.intersection);
     }
-    value.extraInfo = object["extraInfo"].toMap();
+    QScriptValue surfaceNormal = object.property("surfaceNormal");
+    if (surfaceNormal.isValid()) {
+        vec3FromScriptValue(surfaceNormal, value.surfaceNormal);
+    }
+    value.extraInfo = object.property("extraInfo").toVariant().toMap();
 }
 
 bool Overlays::isLoaded(OverlayID id) {
diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h
index 33768416fe..21b9e93648 100644
--- a/interface/src/ui/overlays/Overlays.h
+++ b/interface/src/ui/overlays/Overlays.h
@@ -59,7 +59,7 @@ class RayToOverlayIntersectionResult {
 public:
     bool intersects { false };
     OverlayID overlayID { UNKNOWN_OVERLAY_ID };
-    float distance { 0 };
+    float distance { 0.0f };
     BoxFace face { UNKNOWN_FACE };
     glm::vec3 surfaceNormal;
     glm::vec3 intersection;
@@ -73,8 +73,8 @@ class ParabolaToOverlayIntersectionResult {
 public:
     bool intersects { false };
     OverlayID overlayID { UNKNOWN_OVERLAY_ID };
-    float distance { 0 };
-    float parabolicDistance { 0 };
+    float distance { 0.0f };
+    float parabolicDistance { 0.0f };
     BoxFace face { UNKNOWN_FACE };
     glm::vec3 surfaceNormal;
     glm::vec3 intersection;
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index fc72f34304..abdac838b6 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -2555,15 +2555,18 @@ glm::mat4 AvatarData::getControllerRightHandMatrix() const {
     return _controllerRightHandMatrixCache.get();
 }
 
-
 QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& value) {
     QScriptValue obj = engine->newObject();
     obj.setProperty("intersects", value.intersects);
     QScriptValue avatarIDValue = quuidToScriptValue(engine, value.avatarID);
     obj.setProperty("avatarID", avatarIDValue);
     obj.setProperty("distance", value.distance);
+    obj.setProperty("face", boxFaceToString(value.face));
+
     QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
     obj.setProperty("intersection", intersection);
+    QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal);
+    obj.setProperty("surfaceNormal", surfaceNormal);
     obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo));
     return obj;
 }
@@ -2573,10 +2576,16 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra
     QScriptValue avatarIDValue = object.property("avatarID");
     quuidFromScriptValue(avatarIDValue, value.avatarID);
     value.distance = object.property("distance").toVariant().toFloat();
+    value.face = boxFaceFromString(object.property("face").toVariant().toString());
+
     QScriptValue intersection = object.property("intersection");
     if (intersection.isValid()) {
         vec3FromScriptValue(intersection, value.intersection);
     }
+    QScriptValue surfaceNormal = object.property("surfaceNormal");
+    if (surfaceNormal.isValid()) {
+        vec3FromScriptValue(surfaceNormal, value.surfaceNormal);
+    }
     value.extraInfo = object.property("extraInfo").toVariant().toMap();
 }
 
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index 5e799b8401..0f850aaf24 100644
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -1527,7 +1527,9 @@ public:
     bool intersects { false };
     QUuid avatarID;
     float distance { 0.0f };
+    BoxFace face;
     glm::vec3 intersection;
+    glm::vec3 surfaceNormal;
     QVariantMap extraInfo;
 };
 Q_DECLARE_METATYPE(RayToAvatarIntersectionResult)
@@ -1540,7 +1542,9 @@ public:
     QUuid avatarID;
     float distance { 0.0f };
     float parabolicDistance { 0.0f };
+    BoxFace face;
     glm::vec3 intersection;
+    glm::vec3 surfaceNormal;
     QVariantMap extraInfo;
 };
 
diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
index f9fed5ae8b..2de6316d74 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
@@ -567,8 +567,7 @@ public:
 bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
                                                               OctreeElementPointer& element,
                                                               float& distance, BoxFace& face, glm::vec3& surfaceNormal,
-                                                              QVariantMap& extraInfo, bool precisionPicking) const
-{
+                                                              QVariantMap& extraInfo, bool precisionPicking) const {
     // TODO -- correctly pick against marching-cube generated meshes
     if (!precisionPicking) {
         // just intersect with bounding box
@@ -614,6 +613,87 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
     return hit;
 }
 
+bool RenderablePolyVoxEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
+                                                                   const glm::vec3& acceleration, OctreeElementPointer& element,
+                                                                   float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal,
+                                                                   QVariantMap& extraInfo, bool precisionPicking) const {
+    // TODO -- correctly pick against marching-cube generated meshes
+    if (!precisionPicking) {
+        // just intersect with bounding box
+        return true;
+    }
+
+    glm::mat4 wtvMatrix = worldToVoxelMatrix();
+    glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f);
+    glm::vec4 velocityInVoxel = wtvMatrix * glm::vec4(velocity, 0.0f);
+    glm::vec4 accelerationInVoxel = wtvMatrix * glm::vec4(acceleration, 0.0f);
+
+    // find the first intersection with the voxel bounding box (slightly enlarged so we can catch voxels that touch the sides)
+    bool success;
+    glm::vec3 center = getCenterPosition(success);
+    glm::vec3 dimensions = getScaledDimensions();
+    const float FIRST_BOX_HALF_SCALE = 0.51f;
+    AABox voxelBox1(wtvMatrix * vec4(center - FIRST_BOX_HALF_SCALE * dimensions, 1.0f),
+                    wtvMatrix * vec4(2.0f * FIRST_BOX_HALF_SCALE * dimensions, 0.0f));
+    bool hit1;
+    float parabolicDistance1;
+    // If we're starting inside the box, our first point is originInVoxel
+    if (voxelBox1.contains(originInVoxel)) {
+        parabolicDistance1 = 0.0f;
+        hit1 = true;
+    } else {
+        BoxFace face1;
+        glm::vec3 surfaceNormal1;
+        hit1 = voxelBox1.findParabolaIntersection(glm::vec3(originInVoxel), glm::vec3(velocityInVoxel), glm::vec3(accelerationInVoxel),
+                                                  parabolicDistance1, face1, surfaceNormal1);
+    }
+
+    if (hit1) {
+        // find the second intersection, which should be with the inside of the box (use a slightly large box again)
+        const float SECOND_BOX_HALF_SCALE = 0.52f;
+        AABox voxelBox2(wtvMatrix * vec4(center - SECOND_BOX_HALF_SCALE * dimensions, 1.0f),
+                        wtvMatrix * vec4(2.0f * SECOND_BOX_HALF_SCALE * dimensions, 0.0f));
+        glm::vec4 originInVoxel2 = originInVoxel + velocityInVoxel * parabolicDistance1 + 0.5f * accelerationInVoxel * parabolicDistance1 * parabolicDistance1;
+        glm::vec4 velocityInVoxel2 = velocityInVoxel + accelerationInVoxel * parabolicDistance1;
+        glm::vec4 accelerationInVoxel2 = accelerationInVoxel;
+        float parabolicDistance2;
+        BoxFace face2;
+        glm::vec3 surfaceNormal2;
+        // this should always be true
+        if (voxelBox2.findParabolaIntersection(glm::vec3(originInVoxel2), glm::vec3(velocityInVoxel2), glm::vec3(accelerationInVoxel2),
+                                               parabolicDistance2, face2, surfaceNormal2)) {
+            const int MAX_SECTIONS = 15;
+            PolyVox::RaycastResult raycastResult = PolyVox::RaycastResults::Completed;
+            glm::vec4 result = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
+            glm::vec4 segmentStartVoxel = originInVoxel2;
+            for (int i = 0; i < MAX_SECTIONS; i++) {
+                float t = parabolicDistance2 * ((float)(i + 1)) / ((float)MAX_SECTIONS);
+                glm::vec4 segmentEndVoxel = originInVoxel2 + velocityInVoxel2 * t + 0.5f * accelerationInVoxel2 * t * t;
+                raycastResult = doRayCast(segmentStartVoxel, segmentEndVoxel, result);
+                if (raycastResult != PolyVox::RaycastResults::Completed) {
+                    // We hit something!
+                    break;
+                }
+                segmentStartVoxel = segmentEndVoxel;
+            }
+
+            if (raycastResult == PolyVox::RaycastResults::Completed) {
+                // the parabola completed its path -- nothing was hit.
+                return false;
+            }
+
+            glm::vec3 result3 = glm::vec3(result);
+
+            AABox voxelBox;
+            voxelBox += result3 - Vectors::HALF;
+            voxelBox += result3 + Vectors::HALF;
+
+            return voxelBox.findParabolaIntersection(glm::vec3(originInVoxel), glm::vec3(velocityInVoxel), glm::vec3(accelerationInVoxel),
+                                                     parabolicDistance, face, surfaceNormal);
+        }
+    }
+    return false;
+}
 
 PolyVox::RaycastResult RenderablePolyVoxEntityItem::doRayCast(glm::vec4 originInVoxel,
                                                               glm::vec4 farInVoxel,
diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
index d7b9868fbc..7afb9b41b4 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
@@ -53,9 +53,13 @@ public:
 
     virtual bool supportsDetailedIntersection() const override { return true; }
     virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
-                        OctreeElementPointer& element, float& distance,
-                        BoxFace& face, glm::vec3& surfaceNormal,
-                        QVariantMap& extraInfo, bool precisionPicking) const override;
+                                             OctreeElementPointer& element, float& distance,
+                                             BoxFace& face, glm::vec3& surfaceNormal,
+                                             QVariantMap& extraInfo, bool precisionPicking) const override;
+    virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const vec3& accleration,
+                                                  OctreeElementPointer& element, float& parabolicDistance,
+                                                  BoxFace& face, glm::vec3& surfaceNormal,
+                                                  QVariantMap& extraInfo, bool precisionPicking) const override;
 
     virtual void setVoxelData(const QByteArray& voxelData) override;
     virtual void setVoxelVolumeSize(const glm::vec3& voxelVolumeSize) override;
diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index d2e40eb527..8fd87e068a 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -1050,65 +1050,16 @@ bool EntityScriptingInterface::getDrawZoneBoundaries() const {
 }
 
 QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& value) {
-    PROFILE_RANGE(script_entities, __FUNCTION__);
-
     QScriptValue obj = engine->newObject();
     obj.setProperty("intersects", value.intersects);
     obj.setProperty("accurate", value.accurate);
     QScriptValue entityItemValue = EntityItemIDtoScriptValue(engine, value.entityID);
     obj.setProperty("entityID", entityItemValue);
-
     obj.setProperty("distance", value.distance);
-
-    QString faceName = "";
-    // handle BoxFace
-    /**jsdoc
-     * <p>A <code>BoxFace</code> specifies the face of an axis-aligned (AA) box.
-     * <table>
-     *   <thead>
-     *     <tr><th>Value</th><th>Description</th></tr>
-     *   </thead>
-     *   <tbody>
-     *     <tr><td><code>"MIN_X_FACE"</code></td><td>The minimum x-axis face.</td></tr>
-     *     <tr><td><code>"MAX_X_FACE"</code></td><td>The maximum x-axis face.</td></tr>
-     *     <tr><td><code>"MIN_Y_FACE"</code></td><td>The minimum y-axis face.</td></tr>
-     *     <tr><td><code>"MAX_Y_FACE"</code></td><td>The maximum y-axis face.</td></tr>
-     *     <tr><td><code>"MIN_Z_FACE"</code></td><td>The minimum z-axis face.</td></tr>
-     *     <tr><td><code>"MAX_Z_FACE"</code></td><td>The maximum z-axis face.</td></tr>
-     *     <tr><td><code>"UNKNOWN_FACE"</code></td><td>Unknown value.</td></tr>
-     *   </tbody>
-     * </table>
-     * @typedef {string} BoxFace
-     */
-    //  FIXME: Move enum to string function to BoxBase.cpp.
-    switch (value.face) {
-        case MIN_X_FACE:
-            faceName = "MIN_X_FACE";
-            break;
-        case MAX_X_FACE:
-            faceName = "MAX_X_FACE";
-            break;
-        case MIN_Y_FACE:
-            faceName = "MIN_Y_FACE";
-            break;
-        case MAX_Y_FACE:
-            faceName = "MAX_Y_FACE";
-            break;
-        case MIN_Z_FACE:
-            faceName = "MIN_Z_FACE";
-            break;
-        case MAX_Z_FACE:
-            faceName = "MAX_Z_FACE";
-            break;
-        case UNKNOWN_FACE:
-            faceName = "UNKNOWN_FACE";
-            break;
-    }
-    obj.setProperty("face", faceName);
+    obj.setProperty("face", boxFaceToString(value.face));
 
     QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
     obj.setProperty("intersection", intersection);
-
     QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal);
     obj.setProperty("surfaceNormal", surfaceNormal);
     obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo));
@@ -1116,29 +1067,13 @@ QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, c
 }
 
 void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& value) {
-    PROFILE_RANGE(script_entities, __FUNCTION__);
-
     value.intersects = object.property("intersects").toVariant().toBool();
     value.accurate = object.property("accurate").toVariant().toBool();
     QScriptValue entityIDValue = object.property("entityID");
-    // EntityItemIDfromScriptValue(entityIDValue, value.entityID);
     quuidFromScriptValue(entityIDValue, value.entityID);
     value.distance = object.property("distance").toVariant().toFloat();
+    value.face = boxFaceFromString(object.property("face").toVariant().toString());
 
-    QString faceName = object.property("face").toVariant().toString();
-    if (faceName == "MIN_X_FACE") {
-        value.face = MIN_X_FACE;
-    } else if (faceName == "MAX_X_FACE") {
-        value.face = MAX_X_FACE;
-    } else if (faceName == "MIN_Y_FACE") {
-        value.face = MIN_Y_FACE;
-    } else if (faceName == "MAX_Y_FACE") {
-        value.face = MAX_Y_FACE;
-    } else if (faceName == "MIN_Z_FACE") {
-        value.face = MIN_Z_FACE;
-    } else {
-        value.face = MAX_Z_FACE;
-    };
     QScriptValue intersection = object.property("intersection");
     if (intersection.isValid()) {
         vec3FromScriptValue(intersection, value.intersection);
diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h
index d7d86fc489..a166d513d3 100644
--- a/libraries/entities/src/EntityScriptingInterface.h
+++ b/libraries/entities/src/EntityScriptingInterface.h
@@ -80,9 +80,7 @@ public:
     glm::vec3 surfaceNormal;
     QVariantMap extraInfo;
 };
-
 Q_DECLARE_METATYPE(RayToEntityIntersectionResult)
-
 QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& results);
 void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& results);
 
diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h
index 0a48ed584e..52ec8e8c2d 100644
--- a/libraries/entities/src/PolyLineEntityItem.h
+++ b/libraries/entities/src/PolyLineEntityItem.h
@@ -97,7 +97,7 @@ class PolyLineEntityItem : public EntityItem {
     virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
                                                   const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
                                                   BoxFace& face, glm::vec3& surfaceNormal,
-                                                  QVariantMap& extraInfo, bool precisionPicking) const { return false; }
+                                                  QVariantMap& extraInfo, bool precisionPicking) const override { return false; }
 
     // disable these external interfaces as PolyLineEntities caculate their own dimensions based on the points they contain
     virtual void setRegistrationPoint(const glm::vec3& value) override {}; // FIXME: this is suspicious! 
diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h
index e3f8c48dd1..d2ca4db124 100644
--- a/libraries/entities/src/PolyVoxEntityItem.h
+++ b/libraries/entities/src/PolyVoxEntityItem.h
@@ -50,7 +50,7 @@ class PolyVoxEntityItem : public EntityItem {
     virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
                                                   const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
                                                   BoxFace& face, glm::vec3& surfaceNormal,
-                                                  QVariantMap& extraInfo, bool precisionPicking) const { return false; }
+                                                  QVariantMap& extraInfo, bool precisionPicking) const override { return false; }
 
     virtual void debugDump() const override;
 
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 71312f3bb3..ba0d714f7a 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -516,9 +516,9 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co
                         face = triangleSetFace;
                         bestModelTriangle = triangleSetTriangle;
                         bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix;
-                        glm::vec3 worldIntersectionPoint = meshFrameOrigin + meshFrameVelocity * triangleSetDistance +
+                        glm::vec3 meshIntersectionPoint = meshFrameOrigin + meshFrameVelocity * triangleSetDistance +
                             0.5f * meshFrameAcceleration * triangleSetDistance * triangleSetDistance;
-                        glm::vec3 meshIntersectionPoint = origin + velocity * triangleSetDistance +
+                        glm::vec3 worldIntersectionPoint = origin + velocity * triangleSetDistance +
                             0.5f * acceleration * triangleSetDistance * triangleSetDistance;
                         extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint);
                         extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint);
diff --git a/libraries/render-utils/src/parabola.slv b/libraries/render-utils/src/parabola.slv
index a8c892ab00..c40fc89302 100644
--- a/libraries/render-utils/src/parabola.slv
+++ b/libraries/render-utils/src/parabola.slv
@@ -18,6 +18,8 @@ layout(std140) uniform parabolaData {
     vec3 acceleration;
     float width;
     vec4 color;
+    int numSections;
+    ivec3 spare;
 };
 
 out vec4 _color;
@@ -25,8 +27,7 @@ out vec4 _color;
 void main(void) {
     _color = color;
 
-    const int NUM_SECTIONS = 25; // must match value in ParabolaPointer.cpp
-    float t = parabolicDistance * (floor(gl_VertexID / 2) / float(NUM_SECTIONS));
+    float t = parabolicDistance * (floor(gl_VertexID / 2) / float(numSections));
 
     vec4 pos = vec4(velocity * t + 0.5 * acceleration * t * t, 1);
     const float EPSILON = 0.00001;
diff --git a/libraries/shared/src/BoxBase.cpp b/libraries/shared/src/BoxBase.cpp
new file mode 100644
index 0000000000..0b790dc2b0
--- /dev/null
+++ b/libraries/shared/src/BoxBase.cpp
@@ -0,0 +1,46 @@
+//
+//  Created by Sam Gondelman on 7/20/18
+//  Copyright 2018 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "BoxBase.h"
+
+QString boxFaceToString(BoxFace face) {
+    switch (face) {
+        case MIN_X_FACE:
+            return "MIN_X_FACE";
+        case MAX_X_FACE:
+            return "MAX_X_FACE";
+        case MIN_Y_FACE:
+            return "MIN_Y_FACE";
+        case MAX_Y_FACE:
+            return "MAX_Y_FACE";
+        case MIN_Z_FACE:
+            return "MIN_Z_FACE";
+        case MAX_Z_FACE:
+            return "MAX_Z_FACE";
+        default:
+            return "UNKNOWN_FACE";
+    }
+}
+
+BoxFace boxFaceFromString(const QString& face) {
+    if (face == "MIN_X_FACE") {
+        return MIN_X_FACE;
+    } else if (face == "MAX_X_FACE") {
+        return MAX_X_FACE;
+    } else if (face == "MIN_Y_FACE") {
+        return MIN_Y_FACE;
+    } else if (face == "MAX_Y_FACE") {
+        return MAX_Y_FACE;
+    } else if (face == "MIN_Z_FACE") {
+        return MIN_Z_FACE;
+    } else if (face == "MAX_Z_FACE") {
+        return MAX_Z_FACE;
+    } else {
+        return UNKNOWN_FACE;
+    }
+}
\ No newline at end of file
diff --git a/libraries/shared/src/BoxBase.h b/libraries/shared/src/BoxBase.h
index 7f1dd4d34c..9bc2115d9e 100644
--- a/libraries/shared/src/BoxBase.h
+++ b/libraries/shared/src/BoxBase.h
@@ -16,7 +16,26 @@
 #define hifi_BoxBase_h
 
 #include <glm/glm.hpp>
+#include <QString>
 
+/**jsdoc
+* <p>A <code>BoxFace</code> specifies the face of an axis-aligned (AA) box.
+* <table>
+*   <thead>
+*     <tr><th>Value</th><th>Description</th></tr>
+*   </thead>
+*   <tbody>
+*     <tr><td><code>"MIN_X_FACE"</code></td><td>The minimum x-axis face.</td></tr>
+*     <tr><td><code>"MAX_X_FACE"</code></td><td>The maximum x-axis face.</td></tr>
+*     <tr><td><code>"MIN_Y_FACE"</code></td><td>The minimum y-axis face.</td></tr>
+*     <tr><td><code>"MAX_Y_FACE"</code></td><td>The maximum y-axis face.</td></tr>
+*     <tr><td><code>"MIN_Z_FACE"</code></td><td>The minimum z-axis face.</td></tr>
+*     <tr><td><code>"MAX_Z_FACE"</code></td><td>The maximum z-axis face.</td></tr>
+*     <tr><td><code>"UNKNOWN_FACE"</code></td><td>Unknown value.</td></tr>
+*   </tbody>
+* </table>
+* @typedef {string} BoxFace
+*/
 enum BoxFace {
     MIN_X_FACE,
     MAX_X_FACE,
@@ -27,6 +46,9 @@ enum BoxFace {
     UNKNOWN_FACE
 };
 
+QString boxFaceToString(BoxFace face);
+BoxFace boxFaceFromString(const QString& face);
+
 enum BoxVertex {
     BOTTOM_LEFT_NEAR   = 0,
     BOTTOM_RIGHT_NEAR  = 1,
diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp
index f34473a5d8..963294a360 100644
--- a/libraries/shared/src/GeometryUtil.cpp
+++ b/libraries/shared/src/GeometryUtil.cpp
@@ -952,7 +952,7 @@ void checkPossibleParabolicIntersectionWithTriangle(float t, float& minDistance,
 
 bool findParabolaTriangleIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
     const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& parabolicDistance, bool allowBackface) {
-    glm::vec3 normal = glm::cross(v2 - v1, v0 - v1);
+    glm::vec3 normal = glm::normalize(glm::cross(v2 - v1, v0 - v1));
 
     // We transform the parabola and triangle so that the triangle is in the plane z = 0, with v0 at the origin
     glm::quat inverseRot;
@@ -1092,7 +1092,7 @@ inline float parabolaVelocityAtT(float velocity, float acceleration, float t) {
 bool findParabolaAABoxIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
                                    const glm::vec3& corner, const glm::vec3& scale, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal) {
     float minDistance = FLT_MAX;
-    BoxFace minFace;
+    BoxFace minFace = UNKNOWN_FACE;
     glm::vec3 minNormal;
     glm::vec2 possibleDistances;
     float a, b, c;
@@ -1720,8 +1720,8 @@ unsigned int solveP3(float* x, float a, float b, float c) {
         a /= 3.0f;
         q = -2.0f * sqrtf(q);
         x[0] = q * cosf(t / 3.0f) - a;
-        x[1] = q * cosf((t + 2.0f * M_PI) / 3.0f) - a;
-        x[2] = q * cosf((t - 2.0f * M_PI) / 3.0f) - a;
+        x[1] = q * cosf((t + 2.0f * (float)M_PI) / 3.0f) - a;
+        x[2] = q * cosf((t - 2.0f * (float)M_PI) / 3.0f) - a;
         return 3;
     } else {
         A = -powf(fabsf(r) + sqrtf(r2 - q3), 1.0f / 3.0f);
@@ -1755,27 +1755,27 @@ bool solve_quartic(float a, float b, float c, float d, glm::vec4& roots) {
 
     y = px3[0];
     if (iZeroes != 1) {
-        if (fabs(px3[1]) > fabs(y)) {
+        if (fabsf(px3[1]) > fabsf(y)) {
             y = px3[1];
         }
-        if (fabs(px3[2]) > fabs(y)) {
+        if (fabsf(px3[2]) > fabsf(y)) {
             y = px3[2];
         }
     }
 
     D = y * y - 4.0f * d;
-    if (fabs(D) < EPSILON) {
+    if (fabsf(D) < EPSILON) {
         q1 = q2 = 0.5f * y;
         D = a * a - 4.0f * (b - y);
-        if (fabs(D) < EPSILON) {
+        if (fabsf(D) < EPSILON) {
             p1 = p2 = 0.5f * a;
         } else {
-            sqD = sqrt(D);
+            sqD = sqrtf(D);
             p1 = 0.5f * (a + sqD);
             p2 = 0.5f * (a - sqD);
         }
     } else {
-        sqD = sqrt(D);
+        sqD = sqrtf(D);
         q1 = 0.5f * (y + sqD);
         q2 = 0.5f * (y - sqD);
         p1 = (a * q1 - c) / (q1 - q2);
@@ -1786,10 +1786,10 @@ bool solve_quartic(float a, float b, float c, float d, glm::vec4& roots) {
     D = p1 * p1 - 4.0f * q1;
     if (D < 0.0f) {
         x1.real(-0.5f * p1);
-        x1.imag(0.5f * sqrt(-D));
+        x1.imag(0.5f * sqrtf(-D));
         x2 = std::conj(x1);
     } else {
-        sqD = sqrt(D);
+        sqD = sqrtf(D);
         x1.real(0.5f * (-p1 + sqD));
         x2.real(0.5f * (-p1 - sqD));
     }
@@ -1797,10 +1797,10 @@ bool solve_quartic(float a, float b, float c, float d, glm::vec4& roots) {
     D = p2 * p2 - 4.0f * q2;
     if (D < 0.0f) {
         x3.real(-0.5f * p2);
-        x3.imag(0.5f * sqrt(-D));
+        x3.imag(0.5f * sqrtf(-D));
         x4 = std::conj(x3);
     } else {
-        sqD = sqrt(D);
+        sqD = sqrtf(D);
         x3.real(0.5f * (-p2 + sqD));
         x4.real(0.5f * (-p2 - sqD));
     }
diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js
index 55162ae3e9..e926a70bee 100644
--- a/scripts/system/controllers/controllerModules/teleport.js
+++ b/scripts/system/controllers/controllerModules/teleport.js
@@ -100,7 +100,7 @@ Script.include("/~/system/libraries/controllers.js");
         {name: "teleport", path: teleportPath, end: teleportEnd},
         {name: "seat", path: seatPath, end: seatEnd}];
 
-    var DEFAULT_DISTANCE = 10;
+    var DEFAULT_DISTANCE = 4.0;
     var teleportDefaultRenderStates = [{name: "cancel", distance: DEFAULT_DISTANCE, path: cancelPath}];
 
     var coolInTimeout = null;
@@ -151,7 +151,8 @@ Script.include("/~/system/libraries/controllers.js");
             accelerationAxis: accelerationAxis,
             rotateAccelerationWithAvatar: true,
             renderStates: teleportRenderStates,
-            defaultRenderStates: teleportDefaultRenderStates
+            defaultRenderStates: teleportDefaultRenderStates,
+            maxDistance: 4.0
         });
         this.teleportParabolaHandInvisible = Pointers.createPointer(PickType.Parabola, {
             joint: (_this.hand === RIGHT_HAND) ? "RightHand" : "LeftHand",
@@ -163,7 +164,8 @@ Script.include("/~/system/libraries/controllers.js");
             speed: speed,
             accelerationAxis: accelerationAxis,
             rotateAccelerationWithAvatar: true,
-            renderStates: teleportRenderStates
+            renderStates: teleportRenderStates,
+            maxDistance: 4.0
         });
         this.teleportParabolaHeadVisible = Pointers.createPointer(PickType.Parabola, {
             joint: "Avatar",
@@ -176,7 +178,8 @@ Script.include("/~/system/libraries/controllers.js");
             accelerationAxis: accelerationAxis,
             rotateAccelerationWithAvatar: true,
             renderStates: teleportRenderStates,
-            defaultRenderStates: teleportDefaultRenderStates
+            defaultRenderStates: teleportDefaultRenderStates,
+            maxDistance: 4.0
         });
         this.teleportParabolaHeadInvisible = Pointers.createPointer(PickType.Parabola, {
             joint: "Avatar",
@@ -188,7 +191,8 @@ Script.include("/~/system/libraries/controllers.js");
             speed: speed,
             accelerationAxis: accelerationAxis,
             rotateAccelerationWithAvatar: true,
-            renderStates: teleportRenderStates
+            renderStates: teleportRenderStates,
+            maxDistance: 4.0
         });
 
         this.cleanup = function() {
@@ -430,7 +434,7 @@ Script.include("/~/system/libraries/controllers.js");
         }
 
         var surfaceNormal = result.surfaceNormal;
-        var angle = Math.abs(Math.acos(Vec3.dot(surfaceNormal, Quat.getUp(MyAvatar.orientation)))) * (180.0 / Math.PI);
+        var angle = Math.acos(Vec3.dot(surfaceNormal, Quat.getUp(MyAvatar.orientation))) * (180.0 / Math.PI);
 
         if (angle > MAX_ANGLE_FROM_UP_TO_TELEPORT ||
             Vec3.distance(MyAvatar.position, result.intersection) <= TELEPORT_CANCEL_RANGE * MyAvatar.sensorToWorldScale) {