From 52bbb4d2abac51afe38fe5050a168464625d1ce3 Mon Sep 17 00:00:00 2001
From: Seth Alves <seth.alves@gmail.com>
Date: Mon, 27 Aug 2018 12:47:17 -0700
Subject: [PATCH] fix ray-pick results on entities with non-default
 registration point.  clean up grab scripts to correctly use this fix.

---
 interface/src/raypick/PathPointer.cpp         |  4 +--
 .../controllerModules/farActionGrabEntity.js  | 18 +++--------
 .../farActionGrabEntityDynOnly.js             | 17 +++-------
 .../controllerModules/farParentGrabEntity.js  | 18 +++--------
 .../libraries/controllerDispatcherUtils.js    | 32 +++++++++++++++++--
 5 files changed, 45 insertions(+), 44 deletions(-)

diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp
index 685611d77b..d434c667de 100644
--- a/interface/src/raypick/PathPointer.cpp
+++ b/interface/src/raypick/PathPointer.cpp
@@ -105,7 +105,7 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick
                 glm::mat4 entityMat = createMatFromQuatAndPos(props.getRotation(), props.getPosition());
                 glm::mat4 finalPosAndRotMat = entityMat * _lockEndObject.offsetMat;
                 pos = extractTranslation(finalPosAndRotMat);
-                rot = glmExtractRotation(finalPosAndRotMat);
+                rot = props.getRotation();
                 dim = props.getDimensions();
                 registrationPoint = props.getRegistrationPoint();
             }
@@ -350,4 +350,4 @@ glm::vec2 PathPointer::findPos2D(const PickedObject& pickedObject, const glm::ve
     default:
         return glm::vec2(NAN);
     }
-}
\ No newline at end of file
+}
diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js
index e4563fda14..5e798ed680 100644
--- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js
+++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js
@@ -14,7 +14,8 @@
    PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD,
    DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic,
    getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI
-   Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST
+   Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST,
+   worldPositionToRegistrationFrameMatrix
 */
 
 Script.include("/~/system/libraries/controllerDispatcherUtils.js");
@@ -593,18 +594,9 @@ Script.include("/~/system/libraries/Xform.js");
 
         this.calculateOffset = function(controllerData) {
             if (this.distanceHolding || this.distanceRotating) {
-                var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [
-                    "position",
-                    "rotation"
-                ]);
-                var zeroVector = { x: 0, y: 0, z:0, w: 0 };
-                var intersection = controllerData.rayPicks[this.hand].intersection;
-                var intersectionMat = new Xform(zeroVector, intersection);
-                var modelMat = new Xform(targetProps.rotation, targetProps.position);
-                var modelMatInv = modelMat.inv();
-                var xformMat = Xform.mul(modelMatInv, intersectionMat);
-                var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos);
-                return offsetMat;
+                var targetProps = Entities.getEntityProperties(this.targetObject.entityID,
+                                                               [ "position", "rotation", "registrationPoint", "dimensions" ]);
+                return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection);
             }
             return undefined;
         };
diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js
index a080e75325..78abcb9b20 100644
--- a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js
+++ b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js
@@ -12,7 +12,7 @@
    makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE,
    TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD,
    Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST,
-   Uuid
+   Uuid, worldPositionToRegistrationFrameMatrix
 */
 
 Script.include("/~/system/libraries/controllerDispatcherUtils.js");
@@ -572,18 +572,9 @@ Script.include("/~/system/libraries/Xform.js");
 
         this.calculateOffset = function(controllerData) {
             if (this.distanceHolding || this.distanceRotating) {
-                var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [
-                    "position",
-                    "rotation"
-                ]);
-                var zeroVector = { x: 0, y: 0, z:0, w: 0 };
-                var intersection = controllerData.rayPicks[this.hand].intersection;
-                var intersectionMat = new Xform(zeroVector, intersection);
-                var modelMat = new Xform(targetProps.rotation, targetProps.position);
-                var modelMatInv = modelMat.inv();
-                var xformMat = Xform.mul(modelMatInv, intersectionMat);
-                var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos);
-                return offsetMat;
+                var targetProps = Entities.getEntityProperties(this.targetObject.entityID,
+                                                               [ "position", "rotation", "registrationPoint", "dimensions" ]);
+                return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection);
             }
             return undefined;
         };
diff --git a/scripts/system/controllers/controllerModules/farParentGrabEntity.js b/scripts/system/controllers/controllerModules/farParentGrabEntity.js
index 439b5e5f51..a9ec246a32 100644
--- a/scripts/system/controllers/controllerModules/farParentGrabEntity.js
+++ b/scripts/system/controllers/controllerModules/farParentGrabEntity.js
@@ -11,7 +11,8 @@
    Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC,
    HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation,
    projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager,
-   getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent
+   getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent,
+   worldPositionToRegistrationFrameMatrix
 */
 
 Script.include("/~/system/libraries/controllerDispatcherUtils.js");
@@ -615,18 +616,9 @@ Script.include("/~/system/libraries/Xform.js");
 
         this.calculateOffset = function(controllerData) {
             if (this.distanceHolding || this.distanceRotating) {
-                var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [
-                    "position",
-                    "rotation"
-                ]);
-                var zeroVector = { x: 0, y: 0, z:0, w: 0 };
-                var intersection = controllerData.rayPicks[this.hand].intersection;
-                var intersectionMat = new Xform(zeroVector, intersection);
-                var modelMat = new Xform(targetProps.rotation, targetProps.position);
-                var modelMatInv = modelMat.inv();
-                var xformMat = Xform.mul(modelMatInv, intersectionMat);
-                var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos);
-                return offsetMat;
+                var targetProps = Entities.getEntityProperties(this.targetObject.entityID,
+                                                               [ "position", "rotation", "registrationPoint", "dimensions" ]);
+                return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection);
             }
             return undefined;
         };
diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js
index a386dcf5b4..c34fd76802 100644
--- a/scripts/system/libraries/controllerDispatcherUtils.js
+++ b/scripts/system/libraries/controllerDispatcherUtils.js
@@ -5,7 +5,7 @@
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 
-/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform,
+/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, Mat4,
    Selection, Uuid,
    MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true,
    HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true,
@@ -58,7 +58,8 @@
    highlightTargetEntity:true,
    clearHighlightedEntities:true,
    unhighlightTargetEntity:true,
-   distanceBetweenEntityLocalPositionAndBoundingBox: true
+   distanceBetweenEntityLocalPositionAndBoundingBox: true,
+   worldPositionToRegistrationFrameMatrix: true
 */
 
 MSECS_PER_SEC = 1000.0;
@@ -487,6 +488,30 @@ entityIsFarGrabbedByOther = function(entityID) {
     return false;
 };
 
+
+worldPositionToRegistrationFrameMatrix = function(wptrProps, pos) {
+    // get world matrix for intersection point
+    var intersectionMat = new Xform({ x: 0, y: 0, z:0, w: 1 }, pos);
+
+    // calculate world matrix for registrationPoint addjusted entity
+    var DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 };
+    var regRatio = Vec3.subtract(DEFAULT_REGISTRATION_POINT, wptrProps.registrationPoint);
+    var regOffset = Vec3.multiplyVbyV(regRatio, wptrProps.dimensions);
+    var regOffsetRot = Vec3.multiplyQbyV(wptrProps.rotation, regOffset);
+    var modelMat = new Xform(wptrProps.rotation, Vec3.sum(wptrProps.position, regOffsetRot));
+
+    // get inverse of model matrix
+    var modelMatInv = modelMat.inv();
+
+    // transform world intersection point into object's registrationPoint frame
+    var xformMat = Xform.mul(modelMatInv, intersectionMat);
+
+    // convert to Mat4
+    var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos);
+    return offsetMat;
+};
+
+
 if (typeof module !== 'undefined') {
     module.exports = {
         makeDispatcherModuleParameters: makeDispatcherModuleParameters,
@@ -508,6 +533,7 @@ if (typeof module !== 'undefined') {
         projectOntoEntityXYPlane: projectOntoEntityXYPlane,
         TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE,
         TRIGGER_ON_VALUE: TRIGGER_ON_VALUE,
-        DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST
+        DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST,
+        worldPositionToRegistrationFrameMatrix: worldPositionToRegistrationFrameMatrix
     };
 }