Forum Discussion

lufinkey's avatar
lufinkey
Protege
4 years ago
Solved

Modify current grabbing pose of a HandGrabInteractor dynamically

I'm looking for a simple way to change the current grabbing pose of a HandGrabInteractor as it's being held (for example in response to a button press or change in the object etc). I have a dirty way of setting it using reflection but it always realigns the hand to the wrong position. Is there a correct / easier way to do this?

  • After spending WAY too much time trying to figure this out, I was able to get it working with the following code:

     

     

    public bool ApplyNewPoseToGrabbingInteractor(HandGrabInteractor interactor, HandGrabInteractable interactableWithNewGrabPose) {
    	// ensure we have a selected interactable and a valid snap address
    	if (interactor.SelectedInteractable == null) {
    		return false;
    	}
    	var snapAddress = (SnapAddress<HandGrabInteractable>)MPUtils.GetMemberValue(interactor, "_currentSnap");
    	if (SnapAddress<HandGrabInteractable>.IsNullOrInvalid(snapAddress)) {
    		return false;
    	}
    	var oldSnapPoint = snapAddress.SnapPoint;
    
    	// get grab properties
    	var hand = interactor.Hand;
    	var trackedPinchPose = (Pose)MPUtils.GetMemberValue(interactor, "_trackedPinchPose");
    	var trackedGripPose = (Pose)MPUtils.GetMemberValue(interactor, "_trackedGripPose");
    
    	// get previous snap address properties
    	var snappingInteractable = snapAddress.Interactable;
    	var handPose = (HandPose)MPUtils.GetMemberValue(interactor, "_cachedBestHandPose");
    	var snapPoint = (Pose)MPUtils.GetMemberValue(interactor, "_cachedBestSnapPoint");
    	var usePinchPoint = snapAddress.SnappedToPinch;
    
    	// update grab point values
    	var grabPoint = usePinchPoint ? trackedPinchPose : trackedGripPose;
    	bool poseFound = interactableWithNewGrabPose.CalculateBestPose(grabPoint, hand.Scale, hand.Handedness,
    		ref handPose, ref snapPoint, out bool usesHandPose, out float poseScore);
    	MPUtils.SetMemberValue(interactor, "_cachedBestHandPose", handPose);
    	MPUtils.SetMemberValue(interactor, "_cachedBestSnapPoint", snapPoint);
    
    	// apply pose if able
    	if (poseFound) {
    		var snapHandPose = usesHandPose ? handPose : null;
    		snapAddress.Set(snappingInteractable, snapHandPose, snapPoint, usePinchPoint);
    		if (interactor.SnapData != null) {
    			MPUtils.SetMemberValue(interactor, "SnapData", snapAddress);
    		}
    		// adjust pointable element position/rotation to compensate for pose difference
    		var pointableElementTransform = (snappingInteractable.PointableElement as Component)?.transform;
    		var snapPointRotOffset = snapPoint.rotation * Quaternion.Inverse(oldSnapPoint.rotation);
    		if (pointableElementTransform != null) {
    			var oldRotation = pointableElementTransform.rotation;
    			var newRotation = oldRotation * Quaternion.Inverse(snapPointRotOffset);
    			pointableElementTransform.position -= -(pointableElementTransform.rotation * oldSnapPoint.position) + (newRotation * snapPoint.position);
    			pointableElementTransform.rotation = newRotation;
    		}
    	}
    
    	return poseFound;
    }

     

     


    The MPUtils.SetMemberValue and MPUtils.GetMemberValue methods are just helper methods to access private member variables via reflection.
    Hopefully this saves someone else the time I spent trying to get this working! And if the oculus team reads this, it would be really awesome if you could add this as a feature! I'm sure this is probably the worst way to accomplish this

    Edit: this was done on oculus interaction sdk v40, for reference

1 Reply

  • After spending WAY too much time trying to figure this out, I was able to get it working with the following code:

     

     

    public bool ApplyNewPoseToGrabbingInteractor(HandGrabInteractor interactor, HandGrabInteractable interactableWithNewGrabPose) {
    	// ensure we have a selected interactable and a valid snap address
    	if (interactor.SelectedInteractable == null) {
    		return false;
    	}
    	var snapAddress = (SnapAddress<HandGrabInteractable>)MPUtils.GetMemberValue(interactor, "_currentSnap");
    	if (SnapAddress<HandGrabInteractable>.IsNullOrInvalid(snapAddress)) {
    		return false;
    	}
    	var oldSnapPoint = snapAddress.SnapPoint;
    
    	// get grab properties
    	var hand = interactor.Hand;
    	var trackedPinchPose = (Pose)MPUtils.GetMemberValue(interactor, "_trackedPinchPose");
    	var trackedGripPose = (Pose)MPUtils.GetMemberValue(interactor, "_trackedGripPose");
    
    	// get previous snap address properties
    	var snappingInteractable = snapAddress.Interactable;
    	var handPose = (HandPose)MPUtils.GetMemberValue(interactor, "_cachedBestHandPose");
    	var snapPoint = (Pose)MPUtils.GetMemberValue(interactor, "_cachedBestSnapPoint");
    	var usePinchPoint = snapAddress.SnappedToPinch;
    
    	// update grab point values
    	var grabPoint = usePinchPoint ? trackedPinchPose : trackedGripPose;
    	bool poseFound = interactableWithNewGrabPose.CalculateBestPose(grabPoint, hand.Scale, hand.Handedness,
    		ref handPose, ref snapPoint, out bool usesHandPose, out float poseScore);
    	MPUtils.SetMemberValue(interactor, "_cachedBestHandPose", handPose);
    	MPUtils.SetMemberValue(interactor, "_cachedBestSnapPoint", snapPoint);
    
    	// apply pose if able
    	if (poseFound) {
    		var snapHandPose = usesHandPose ? handPose : null;
    		snapAddress.Set(snappingInteractable, snapHandPose, snapPoint, usePinchPoint);
    		if (interactor.SnapData != null) {
    			MPUtils.SetMemberValue(interactor, "SnapData", snapAddress);
    		}
    		// adjust pointable element position/rotation to compensate for pose difference
    		var pointableElementTransform = (snappingInteractable.PointableElement as Component)?.transform;
    		var snapPointRotOffset = snapPoint.rotation * Quaternion.Inverse(oldSnapPoint.rotation);
    		if (pointableElementTransform != null) {
    			var oldRotation = pointableElementTransform.rotation;
    			var newRotation = oldRotation * Quaternion.Inverse(snapPointRotOffset);
    			pointableElementTransform.position -= -(pointableElementTransform.rotation * oldSnapPoint.position) + (newRotation * snapPoint.position);
    			pointableElementTransform.rotation = newRotation;
    		}
    	}
    
    	return poseFound;
    }

     

     


    The MPUtils.SetMemberValue and MPUtils.GetMemberValue methods are just helper methods to access private member variables via reflection.
    Hopefully this saves someone else the time I spent trying to get this working! And if the oculus team reads this, it would be really awesome if you could add this as a feature! I'm sure this is probably the worst way to accomplish this

    Edit: this was done on oculus interaction sdk v40, for reference