Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Grab the middle of a rope?
#1
I'm working on a Vive VR project that will have a loop at the top with a rope threaded through it and each side dangling down.  The user will be able to grab any point along the rope with the motion controllers and pull.  With standard joint ropes, this tends to cause all sorts of physics problems, especially if the user pulls too far too quickly or twists the joints around.

I saw the videos of Obi Rope, and it looks like you can easily move the ends of the rope around without a problem, but would something like this be possible - allowing the rope to be grabbed at different points along its length with virtual hands and pulled around?  And if so, could you maybe point me in the right direction of how to go about it?

Thanks!
Reply
#2
(24-08-2017, 04:16 PM)BlueTel Wrote: I'm working on a Vive VR project that will have a loop at the top with a rope threaded through it and each side dangling down.  The user will be able to grab any point along the rope with the motion controllers and pull.  With standard joint ropes, this tends to cause all sorts of physics problems, especially if the user pulls too far too quickly or twists the joints around.

I saw the videos of Obi Rope, and it looks like you can easily move the ends of the rope around without a problem, but would something like this be possible - allowing the rope to be grabbed at different points along its length with virtual hands and pulled around?  And if so, could you maybe point me in the right direction of how to go about it?

Thanks!

Hi there!

There's no difference in grabbing the ends or any other point in the rope. Ropes are basically chains of particles, you can freeze any particle you wish along the rope and move it around. This is done by setting the inverse mass of the particle to zero, and setting its position to whatever you want. See:

http://obi.virtualmethodstudio.com/tutor...icles.html

Keep in mind that absolutely no solver will be able to handle arbitrarily large forces applied to a body, specially not if collisions are involved. You should clamp the maximum force an user is able to exert when pulling on the rope no matter what solver you use (Obi, PhysX, or any other). This is just a limitation of realtime physics: large and sudden forces need higher computation time to be handled with enough precision, so there will always be a threshold that once surpassed, breaks the simulation. Obi is no exception to this.
Reply
#3
(24-08-2017, 05:47 PM)josemendez Wrote: Hi there!

There's no difference in grabbing the ends or any other point in the rope. Ropes are basically chains of particles, you can freeze any particle you wish along the rope and move it around. This is done by setting the inverse mass of the particle to zero, and setting its position to whatever you want. See:

http://obi.virtualmethodstudio.com/tutor...icles.html

Keep in mind that absolutely no solver will be able to handle arbitrarily large forces applied to a body, specially not if collisions are involved. You should clamp the maximum force an user is able to exert when pulling on the rope no matter what solver you use (Obi, PhysX, or any other). This is just a limitation of realtime physics: large and sudden forces need higher computation time to be handled with enough precision, so there will always be a threshold that once surpassed, breaks the simulation. Obi is no exception to this.

I have fully implemented an interaction system for ObiRope using VRTK.

**UPDATED VERSION

Requirments: ObiRope 3.2+ and VRTK 3.3.0 alpha+

Put this on the gameobject with the ObiSolver:

Code:
using System.Collections;
using UnityEngine;
using Obi;
using VRTK;

[System.Serializable]
public class InteractingInfo
{
   [Header("Grab Options")]
   public bool grabbingEnabled;

   [Header("Pin Options")]
   public Vector3 pinOffset;
   [Range(0f, 1f)]
   public float stiffness;

   [HideInInspector]
   public bool isTouchingParticle;
   [HideInInspector]
   public int touchingParticle;
   [HideInInspector]
   public int? grabbedParticle;
   [HideInInspector]
   public int? pinIndex;
   [HideInInspector]
   public VRTK_InteractGrab interactGrab;
   [HideInInspector]
   public Collider collider;
   [HideInInspector]
   public ObiCollider obiCollider;
   [HideInInspector]
   public VRTK_ObiRopeInteraction currentInteraction;

   public void Clear()
   {
       isTouchingParticle = false;
   }
}

[RequireComponent(typeof(ObiSolver))]
public class VRTK_ObiRopeSolver : MonoBehaviour
{
   #region Fields

   [Tooltip("Check if using VRTK_InteractTouch custom collider")]
   public bool customColliders;
   public string colliderObjectName = "Head";

   [Header("Controller Info")]

   public InteractingInfo leftControllerInfo = new InteractingInfo();
   public InteractingInfo rightControllerInfo = new InteractingInfo();

   private ObiSolver solver;

   private GameObject leftController;
   private GameObject rightController;

   #endregion


   #region Mono Events

   private void Awake()
   {
       solver = GetComponent<ObiSolver>();

       VRTK_SDKManager.instance.AddBehaviourToToggleOnLoadedSetupChange(this);
   }

   private void OnDestroy()
   {
       VRTK_SDKManager.instance.RemoveBehaviourToToggleOnLoadedSetupChange(this);
   }

   private void Reset()
   {
       colliderObjectName = "Head";
   }

   private void OnEnable()
   {
       leftController = VRTK.VRTK_DeviceFinder.GetControllerLeftHand(false);
       rightController = VRTK.VRTK_DeviceFinder.GetControllerRightHand(false);

       if (customColliders)
       {
           SetupController(leftController, true);
           SetupController(rightController, false);
       }
       else
       {
           leftController.GetComponent<VRTK_ControllerEvents>().ControllerModelAvailable += ControllerModelAvailiable;
           rightController.GetComponent<VRTK_ControllerEvents>().ControllerModelAvailable += ControllerModelAvailiable;
       }

       if (!ReferenceEquals(solver, null))
       {
           solver.OnCollision += HandleSolverCollision;
       }
   }

   private void OnDisable()
   {
       if (!ReferenceEquals(solver, null))
       {
           solver.OnCollision -= HandleSolverCollision;
       }
   }

   #endregion


   #region Private Methods

   private bool SetupController(GameObject controller, bool isLeft)
   {
       if (controller.transform.childCount == 0) return false;

       Transform colliderObject = customColliders ? controller.transform.Find(colliderObjectName) : controller.transform.GetChild(0).Find(colliderObjectName);
       colliderObject.gameObject.layer = LayerMask.NameToLayer("Default");

       var collider = colliderObject.gameObject.GetComponent<ObiCollider>();

       if (isLeft)
       {
           if (ReferenceEquals(collider, null))
           {
               leftControllerInfo.obiCollider = colliderObject.gameObject.AddComponent<ObiCollider>();
           }
           else
           {
               leftControllerInfo.obiCollider = collider;
           }

           leftControllerInfo.collider = colliderObject.GetComponent<Collider>();

           leftControllerInfo.interactGrab = controller.GetComponent<VRTK_InteractGrab>();
           leftControllerInfo.interactGrab.GrabButtonPressed += HandleGrabButtonPressed;
           leftControllerInfo.interactGrab.GrabButtonReleased += HandleGrabButtonReleased;
       }
       else
       {
           if (ReferenceEquals(collider, null))
           {
               rightControllerInfo.obiCollider = colliderObject.gameObject.AddComponent<ObiCollider>();
           }
           else
           {
               rightControllerInfo.obiCollider = collider;
           }

           rightControllerInfo.collider = colliderObject.GetComponent<Collider>();

           rightControllerInfo.interactGrab = controller.GetComponent<VRTK_InteractGrab>();
           rightControllerInfo.interactGrab.GrabButtonPressed += HandleGrabButtonPressed;
           rightControllerInfo.interactGrab.GrabButtonReleased += HandleGrabButtonReleased;
       }

       return true;
   }

   #endregion


   #region Controller Events

   private void ControllerModelAvailiable(object sender, ControllerInteractionEventArgs e)
   {
       StartCoroutine(ModelLoaded(sender as VRTK_ControllerEvents));
   }

   private IEnumerator ModelLoaded(VRTK_ControllerEvents events)
   {
       yield return new WaitForEndOfFrame();

       bool succeeded = true;

       if (ReferenceEquals(events.gameObject, leftController))
       {
           succeeded = SetupController(events.gameObject as GameObject, true);
       }
       else if (ReferenceEquals(events.gameObject, rightController))
       {
           succeeded = SetupController(events.gameObject as GameObject, false);
       }

       if (!succeeded)
           StartCoroutine(ModelLoaded(events as VRTK_ControllerEvents));
   }

   private void HandleGrabButtonPressed(object sender, ControllerInteractionEventArgs e)
   {
       if (ReferenceEquals(sender, leftControllerInfo.interactGrab) && leftControllerInfo.grabbingEnabled)
       {
           if (leftControllerInfo.isTouchingParticle)
           {
               // Get actor and apply constraint
               leftControllerInfo.currentInteraction.GrabRope(leftControllerInfo);
           }
       }
       else if (ReferenceEquals(sender, rightControllerInfo.interactGrab) && rightControllerInfo.grabbingEnabled)
       {
           if (rightControllerInfo.isTouchingParticle)
           {
               // Get actor and apply constraint
               rightControllerInfo.currentInteraction.GrabRope(rightControllerInfo);
           }
       }
   }

   private void HandleGrabButtonReleased(object sender, ControllerInteractionEventArgs e)
   {
       if (rightControllerInfo.grabbedParticle != null && ReferenceEquals(sender, rightControllerInfo.interactGrab) && rightControllerInfo.grabbingEnabled)
       {
           rightControllerInfo.currentInteraction.ReleaseRope(rightControllerInfo);

           if (leftControllerInfo.pinIndex.HasValue && leftControllerInfo.isTouchingParticle && leftControllerInfo.pinIndex > rightControllerInfo.pinIndex)
               leftControllerInfo.pinIndex--;

           rightControllerInfo.grabbedParticle = null;
           rightControllerInfo.pinIndex = null;

       }
       else if (leftControllerInfo.grabbedParticle != null && ReferenceEquals(sender, leftControllerInfo.interactGrab) && leftControllerInfo.grabbingEnabled)
       {
           leftControllerInfo.currentInteraction.ReleaseRope(leftControllerInfo);

           if (rightControllerInfo.pinIndex.HasValue && rightControllerInfo.isTouchingParticle && rightControllerInfo.pinIndex > leftControllerInfo.pinIndex)
               rightControllerInfo.pinIndex--;

           leftControllerInfo.grabbedParticle = null;
           leftControllerInfo.pinIndex = null;
       }
   }

   #endregion


   #region Obi Solver Events

   private void HandleSolverCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
   {
       if (e == null || e.contacts == null || !rightControllerInfo.grabbingEnabled && !leftControllerInfo.grabbingEnabled) return;

       // Reset every frame
       leftControllerInfo.Clear();
       rightControllerInfo.Clear();

       for (int i = 0; i < e.contacts.Length; ++i)
       {
           var contact = e.contacts[i];
           var pia = solver.particleToActor[e.contacts[i].particle];
           var actor = pia.actor;
           var particleIndex = pia.indexInActor;

           // make sure this is an actual contact
           if (contact.distance < 0.01f)
           {
               Collider collider = ObiColliderBase.idToCollider[contact.other] as Collider;

               if (ReferenceEquals(collider, leftControllerInfo.collider))
               {
                   leftControllerInfo.isTouchingParticle = true;
                   leftControllerInfo.touchingParticle = particleIndex;
                   leftControllerInfo.currentInteraction = actor.gameObject.GetComponent<VRTK_ObiRopeInteraction>();
               }
               else if (ReferenceEquals(collider, rightControllerInfo.collider))
               {
                   rightControllerInfo.isTouchingParticle = true;
                   rightControllerInfo.touchingParticle = particleIndex;
                   rightControllerInfo.currentInteraction = actor.gameObject.GetComponent<VRTK_ObiRopeInteraction>();
               }
           }
       }
   }

   #endregion

}


Put this on the gameobject with the ObiRope:

Code:
using UnityEngine;
using Obi;

[RequireComponent(typeof(ObiPinConstraints))]
public class VRTK_ObiRopeInteraction : MonoBehaviour
{
   #region Fields
   
   public bool isGrabbable = true;
   
   private ObiPinConstraints pinConstraints;

   #endregion


   #region Mono Events

   private void Awake()
   {
       pinConstraints = GetComponent<ObiPinConstraints>();
   }

   private void Reset()
   {
       isGrabbable = true;
   }

   #endregion


   #region Rope Interaction

   public void GrabRope(InteractingInfo interactingInfo)
   {
       if (!isGrabbable) return;

       ObiPinConstraintBatch batch = pinConstraints.GetBatches()[0] as ObiPinConstraintBatch;

       pinConstraints.RemoveFromSolver(null);

       interactingInfo.pinIndex = batch.ConstraintCount;
       interactingInfo.grabbedParticle = interactingInfo.touchingParticle;
       batch.AddConstraint(interactingInfo.grabbedParticle.Value, interactingInfo.obiCollider, interactingInfo.pinOffset, interactingInfo.stiffness);

       pinConstraints.AddToSolver(null);

       pinConstraints.PushDataToSolver();
   }

   public void ReleaseRope(InteractingInfo interactingInfo)
   {
       if (!isGrabbable) return;

       ObiPinConstraintBatch batch = pinConstraints.GetBatches()[0] as ObiPinConstraintBatch;

       pinConstraints.RemoveFromSolver(null);

       if (interactingInfo.pinIndex.HasValue)
           batch.RemoveConstraint(interactingInfo.pinIndex.Value);

       pinConstraints.AddToSolver(null);

       pinConstraints.PushDataToSolver();
   }

   #endregion

}
Reply
#4
Thanks! I may try out your code for another part of the project I'm working on (actually sounds pretty similar - I'm using VRTK, too). For this section, I only needed the user to be able to grab the rope at specific points, so I ended up pinning spheres with trigger colliders along the rope with as little mass as possible and Kinematic turned off. When they grab the sphere, it becomes kinematic and a child of the controller. Seems to work really well for what I'm doing - no weird physics issues like I had with joint ropes. Only minor drawback is there's no rotation to the particles, so you can't twist the rope around, but really not a big deal.
Reply
#5
(02-09-2017, 07:56 PM)niZmo Wrote: I have fully implemented an interaction system for ObiRope using VRTK.

Requirments: ObiRope 3.2+ and VRTK 3.3.0 alpha+


Hi niZmo

Thanks for the share, I think this is what I was looking for on my own project.
Reply
#6
(12-09-2017, 04:12 AM)Parker Wrote: Hi niZmo

Thanks for the share, I think this is what I was looking for on my own project.

Hey, no problem. I've been creating a new interaction system with these two frameworks, and hopefully it covers most of what people want to do with ropes and cloths in VR. It'll be posted on the Made With Obi forum once I finish.
Reply
#7
(12-09-2017, 04:31 AM)niZmo Wrote: Hey, no problem. I've been creating a new interaction system with these two frameworks, and hopefully it covers most of what people want to do with ropes and cloths in VR. It'll be posted on the Made With Obi forum once I finish.

Not to steal this thread, is there an easy way to add mouse support for testing?
Reply
#8
(12-09-2017, 04:31 AM)niZmo Wrote: Hey, no problem. I've been creating a new interaction system with these two frameworks, and hopefully it covers most of what people want to do with ropes and cloths in VR. It'll be posted on the Made With Obi forum once I finish.

Thank you for putting up this script. I've tried it with with Obi Rope 3.4 and VRTK 3.3.0a  (Unity 5.6.3p2) but saw some compiler errors.

Error CS1061 'ObiList<Oni.Contact>' does not contain a definition for 'Length' and no extension method 'Length' accepting a first argument of type 'ObiList<Oni.Contact>' could be found (are you missing a using directive or an assembly reference?) VRTK_ObiRopeSolver.cs 250 Active

Error CS0021 Cannot apply indexing with [] to an expression of type 'IEnumerable<ObiConstraintBatch>' VRTK_ObiRopeInteraction.cs 37 Active

Error CS0021 Cannot apply indexing with [] to an expression of type 'IEnumerable<ObiConstraintBatch>' VRTK_ObiRopeInteraction.cs 54 Active


Do you know if the underlying Obi structures changed after Obi3.2? 

I changed e.contacts.Length to e.contacts.Count and pinConstraints.GetBatches()[0] to pinConstraints.GetBatches().ElementAt(0) which compiled after I added using System.Linq;

I have both the rope and solver on the same object so I attached both VRTK_ObiRopeInteraction.cs and VRTK_ObiRopeInteraction.cs to it, and set the Grabbing Enabled under both left and right controller info. Nothing seems to happen when I try to grab the rope though. I tried to add the usual VRTK interaction components (from the "Setup Interactable Object" wizard) to the rope but that didn't seem to do anything either.

Am I missing something? Thanks.
Reply
#9
I've been out of the scene for like 5 months lol. Things been going on and in the middle of moving houses. I haven't had VR hooked up in a while but I'm going to get my Rift at least from the new house and hook it back up where i am now. Hopefully by tomorrow I can update both libraries VRTK and Obi and fix errors. Before I took a break from coding, I was working on making a library called ObiVR to easily make Obi Rope and Obi Cloth work with VRTK. I did get pretty far back then but never make a Git for it. Ill be getting back to it soon but will try to update this little snippet first.
Reply
#10
Alright I changed some things up, and you were on the right track. Instead of using Linq, Obi has a new method for the constraints called GetFirstBatch(), but the real problem was the colliders hit registration.

Here is the updated code for ObiRope 3.4 and VRTK 3.3.0 alpha. I have also attached the files which is probably easier lol.
This has minimal functionality, a lot of things can be done like attaching 2 particles on grab so the rope doesn't rotate while grabbing with one hand.

Add this to the same gameobject as ObiSolver:

Code:
using System.Collections;
using UnityEngine;
using Obi;
using VRTK;

[System.Serializable]
public class InteractingInfo
{
   [Header("Grab Options")]
   public bool grabbingEnabled = true;

   [Header("Pin Options")]
   public Vector3 pinOffset;
   [Range(0f, 1f)]
   public float stiffness = 1;

   [HideInInspector]
   public bool isTouchingParticle;
   [HideInInspector]
   public int touchingParticle;
   [HideInInspector]
   public int? grabbedParticle;
   [HideInInspector]
   public int? pinIndex;
   [HideInInspector]
   public VRTK_InteractGrab interactGrab;
   [HideInInspector]
   public Collider collider;
   [HideInInspector]
   public ObiCollider obiCollider;
   [HideInInspector]
   public VRTK_ObiInteractableActor currentInteraction;

   public void Clear()
   {
       isTouchingParticle = false;
   }
}

[RequireComponent(typeof(ObiSolver))]
public class VRTK_ObiInteractableSolver : MonoBehaviour
{
   #region Fields


   public Vector3 colliderSize;
   public Vector3 colliderOffset;
   public Vector3 colliderRotation;


   [Header("Controller Info")]

   public InteractingInfo leftControllerInfo = new InteractingInfo();
   public InteractingInfo rightControllerInfo = new InteractingInfo();

   private ObiSolver solver;

   private GameObject leftController;
   private GameObject rightController;

   #endregion


   #region Mono Events

   private void Awake()
   {
       solver = GetComponent<ObiSolver>();

       VRTK_SDKManager.instance.AddBehaviourToToggleOnLoadedSetupChange(this);
   }

   private void OnDestroy()
   {
       VRTK_SDKManager.instance.RemoveBehaviourToToggleOnLoadedSetupChange(this);
   }

   private void Reset()
   {
       colliderSize = new Vector3(0.06f, 0.06f, 0.06f);
       colliderOffset = new Vector3(0, 0, .02f);
       colliderRotation = new Vector3(45, 0, 0);
   }

   private void OnEnable()
   {
       leftController = VRTK.VRTK_DeviceFinder.GetControllerLeftHand(false);
       rightController = VRTK.VRTK_DeviceFinder.GetControllerRightHand(false);

       leftController.GetComponent<VRTK_ControllerEvents>().ControllerModelAvailable += ControllerModelAvailiable;
       rightController.GetComponent<VRTK_ControllerEvents>().ControllerModelAvailable += ControllerModelAvailiable;
       
       if (!ReferenceEquals(solver, null))
       {
           solver.OnCollision += HandleSolverCollision;
       }
   }

   private void OnDisable()
   {
       if (!ReferenceEquals(solver, null))
       {
           solver.OnCollision -= HandleSolverCollision;
       }
   }

   #endregion


   #region Private Methods


   private bool SetupController(GameObject controller, bool isLeft)
   {
       if (controller.transform.childCount == 0) return false;

       GameObject colliderObject = new GameObject();
       colliderObject.name = "ObiCollider";
       colliderObject.layer = LayerMask.NameToLayer("Ignore Raycast");
       colliderObject.transform.SetParent(controller.transform);
       colliderObject.transform.localPosition = colliderOffset;
       colliderObject.transform.localRotation = Quaternion.Euler(colliderRotation);

       var collider = colliderObject.AddComponent<BoxCollider>();
       collider.isTrigger = true;
       collider.size = colliderSize;

       var obiCollider = colliderObject.AddComponent<ObiCollider>();

       if (isLeft)
       {
           if (ReferenceEquals(obiCollider, null))
           {
               leftControllerInfo.obiCollider = colliderObject.gameObject.AddComponent<ObiCollider>();
           }
           else
           {
               leftControllerInfo.obiCollider = obiCollider;
           }

           leftControllerInfo.collider = colliderObject.GetComponent<Collider>();

           leftControllerInfo.interactGrab = controller.GetComponent<VRTK_InteractGrab>();
           leftControllerInfo.interactGrab.GrabButtonPressed += HandleGrabButtonPressed;
           leftControllerInfo.interactGrab.GrabButtonReleased += HandleGrabButtonReleased;
       }
       else
       {
           if (ReferenceEquals(obiCollider, null))
           {
               rightControllerInfo.obiCollider = colliderObject.gameObject.AddComponent<ObiCollider>();
           }
           else
           {
               rightControllerInfo.obiCollider = obiCollider;
           }

           rightControllerInfo.collider = colliderObject.GetComponent<Collider>();

           rightControllerInfo.interactGrab = controller.GetComponent<VRTK_InteractGrab>();
           rightControllerInfo.interactGrab.GrabButtonPressed += HandleGrabButtonPressed;
           rightControllerInfo.interactGrab.GrabButtonReleased += HandleGrabButtonReleased;
       }

       return true;
   }

   #endregion


   #region Controller Events

   private void ControllerModelAvailiable(object sender, ControllerInteractionEventArgs e)
   {
       StartCoroutine(ModelLoaded(sender as VRTK_ControllerEvents));
   }

   private IEnumerator ModelLoaded(VRTK_ControllerEvents events)
   {
       yield return new WaitForEndOfFrame();

       bool succeeded = true;

       if (ReferenceEquals(events.gameObject, leftController))
       {
           succeeded = SetupController(events.gameObject as GameObject, true);
       }
       else if (ReferenceEquals(events.gameObject, rightController))
       {
           succeeded = SetupController(events.gameObject as GameObject, false);
       }

       if (!succeeded)
           StartCoroutine(ModelLoaded(events as VRTK_ControllerEvents));
   }

   private void HandleGrabButtonPressed(object sender, ControllerInteractionEventArgs e)
   {
       if (ReferenceEquals(sender, leftControllerInfo.interactGrab) && leftControllerInfo.grabbingEnabled)
       {
           if (leftControllerInfo.isTouchingParticle)
           {
               // Get actor and apply constraint
               leftControllerInfo.currentInteraction.GrabActor(leftControllerInfo);
           }
       }
       else if (ReferenceEquals(sender, rightControllerInfo.interactGrab) && rightControllerInfo.grabbingEnabled)
       {
           if (rightControllerInfo.isTouchingParticle)
           {
               // Get actor and apply constraint
               rightControllerInfo.currentInteraction.GrabActor(rightControllerInfo);
           }
       }
   }

   private void HandleGrabButtonReleased(object sender, ControllerInteractionEventArgs e)
   {
       if (rightControllerInfo.grabbedParticle != null && ReferenceEquals(sender, rightControllerInfo.interactGrab) && rightControllerInfo.grabbingEnabled)
       {
           rightControllerInfo.currentInteraction.ReleaseActor(rightControllerInfo);

           if (leftControllerInfo.pinIndex.HasValue && leftControllerInfo.isTouchingParticle && leftControllerInfo.pinIndex > rightControllerInfo.pinIndex)
               leftControllerInfo.pinIndex--;

           rightControllerInfo.grabbedParticle = null;
           rightControllerInfo.pinIndex = null;

       }
       else if (leftControllerInfo.grabbedParticle != null && ReferenceEquals(sender, leftControllerInfo.interactGrab) && leftControllerInfo.grabbingEnabled)
       {
           leftControllerInfo.currentInteraction.ReleaseActor(leftControllerInfo);

           if (rightControllerInfo.pinIndex.HasValue && rightControllerInfo.isTouchingParticle && rightControllerInfo.pinIndex > leftControllerInfo.pinIndex)
               rightControllerInfo.pinIndex--;

           leftControllerInfo.grabbedParticle = null;
           leftControllerInfo.pinIndex = null;
       }
   }

   #endregion


   #region Obi Solver Events

   private void HandleSolverCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
   {
       if (e == null || e.contacts == null || !rightControllerInfo.grabbingEnabled && !leftControllerInfo.grabbingEnabled) return;

       // Reset every frame
       leftControllerInfo.Clear();
       rightControllerInfo.Clear();

       for (int i = 0; i < e.contacts.Count; ++i)
       {
           var contact = e.contacts[i];
           var pia = solver.particleToActor[e.contacts[i].particle];
           var actor = pia.actor;
           var particleIndex = pia.indexInActor;

           // make sure this is an actual contact
           if (contact.distance < 0.01f)
           {
               Collider collider = ObiColliderBase.idToCollider[contact.other] as Collider;

               if (ReferenceEquals(collider, leftControllerInfo.collider))
               {
                   leftControllerInfo.isTouchingParticle = true;
                   leftControllerInfo.touchingParticle = particleIndex;
                   leftControllerInfo.currentInteraction = actor.gameObject.GetComponent<VRTK_ObiInteractableActor>();
               }
               else if (ReferenceEquals(collider, rightControllerInfo.collider))
               {
                   rightControllerInfo.isTouchingParticle = true;
                   rightControllerInfo.touchingParticle = particleIndex;
                   rightControllerInfo.currentInteraction = actor.gameObject.GetComponent<VRTK_ObiInteractableActor>();
               }
           }
       }
   }

   #endregion

}

Add this to the same gameobject as your ObiActor:

Code:
using UnityEngine;
using Obi;

[RequireComponent(typeof(ObiPinConstraints))]
public class VRTK_ObiInteractableActor : MonoBehaviour
{
   #region Fields

   public bool isGrabbable = true;

   private ObiPinConstraints pinConstraints;

   #endregion


   #region Mono Events

   private void Awake()
   {
       pinConstraints = GetComponent<ObiPinConstraints>();
   }

   private void Reset()
   {
       isGrabbable = true;
   }

   #endregion


   #region Actor Interaction

   public void GrabActor(InteractingInfo interactingInfo)
   {
       if (!isGrabbable) return;

       ObiPinConstraintBatch batch = pinConstraints.GetFirstBatch() as ObiPinConstraintBatch;

       pinConstraints.RemoveFromSolver(null);

       interactingInfo.pinIndex = batch.ConstraintCount;
       interactingInfo.grabbedParticle = interactingInfo.touchingParticle;
       batch.AddConstraint(interactingInfo.grabbedParticle.Value, interactingInfo.obiCollider, interactingInfo.pinOffset, interactingInfo.stiffness);

       pinConstraints.AddToSolver(null);

       pinConstraints.PushDataToSolver();
   }

   public void ReleaseActor(InteractingInfo interactingInfo)
   {
       if (!isGrabbable) return;

       ObiPinConstraintBatch batch = pinConstraints.GetFirstBatch() as ObiPinConstraintBatch;

       pinConstraints.RemoveFromSolver(null);

       if (interactingInfo.pinIndex.HasValue)
           batch.RemoveConstraint(interactingInfo.pinIndex.Value);

       pinConstraints.AddToSolver(null);

       pinConstraints.PushDataToSolver();
   }

   #endregion

}


Attached Files
.cs   VRTK_ObiInteractableActor.cs (Size: 1.62 KB / Downloads: 40)
.cs   VRTK_ObiInteractableSolver.cs (Size: 9.38 KB / Downloads: 27)
Reply