Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Grab Rope - VR
#1
Hello,
I'm trying to grab a obi rope with my hands in VR (oculus rift). I can't figure out how to do it.... I can't find any tutorial or guidance on how to do it. I spent days trying to implement Nizmo's script posted in 2017 (http://obi.virtualmethodstudio.com/forum...ad-82.html) and it just doesnt work.
I'm a pretty experienced VR developer, so just a high level of what I need to do would suffice.
Any help would be greatly appreciated.
Reply
#2
I join to this question.
I found in older version class ObiGrabCollider that's do this, but in some of versions he was removed.
So guys, do you have something similar to this?
Reply
#3
(23-09-2020, 02:53 PM)Aento Wrote: I join to this question.
I found in older version class ObiGrabCollider that's do this, but in some of versions he was removed.
So guys, do you have something similar to this?

Nizmo's script is needlessly convoluted, as it uses pin constraints (which aren't needed at all since there's no two-way coupling possible between the controller and the rope).

Instead, use the particle API:
http://obi.virtualmethodstudio.com/tutor...icles.html

Setting a particle's inverse mass to zero will make it static, allowing you to override its position any way you need.

You can also use collision callbacks to detect when a particle is in contact with a collider/trigger:
http://obi.virtualmethodstudio.com/tutor...sions.html
Reply
#4
Here's a sample script (for Obi 5.6) using the particle API and contact callbacks to grab any particles in contact with a collider/trigger.

Simply add it to a collider, and drag/drop the solver reference onto it. Then you can call its Grab() and Release() methods to grab any particles in contact with that collider, and release them.

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

/**
* Sample component that makes a collider "grab" any particle it touches (regardless of which Actor it belongs to).
*/
[RequireComponent(typeof(ObiCollider))]
public class ObiContactGrabber : MonoBehaviour
{

    public ObiSolver solver;

    /**
     * Helper class that stores the index of a particle in the solver, its position in the grabber's local space, and its inverse mass previous to being grabbed.
     * This makes it easy to tell if a particle has been grabbed, update its position while grabbing, and restore its mass after being released.
     */
    private class GrabbedParticle : IEqualityComparer<GrabbedParticle>
    {
        public int index;
        public float invMass;
        public Vector3 localPosition;

        public GrabbedParticle(int index, float invMass)
        {
            this.index = index;
            this.invMass = invMass;
        }

        public bool Equals(GrabbedParticle x, GrabbedParticle y)
        {
            return x.index == y.index;
        }

        public int GetHashCode(GrabbedParticle obj)
        {
            return index;
        }
    }

    private ObiSolver.ObiCollisionEventArgs collisionEvent;                                  /**< store the current collision event*/
    private ObiCollider localCollider;                                                           /**< the collider on this gameObject.*/
    private HashSet<GrabbedParticle> grabbedParticles = new HashSet<GrabbedParticle>();          /**< set to store all currently grabbed particles.*/
    private HashSet<ObiActor> grabbedActors = new HashSet<ObiActor>();                 /**< set of softbodies grabbed during this step.*/

    private Matrix4x4 grabber2Solver;
    private Matrix4x4 solver2Grabber;

    private void Awake()
    {
        localCollider = GetComponent<ObiCollider>();
    }

    private void OnEnable()
    {
        if (solver != null)
            solver.OnCollision += Solver_OnCollision;
    }

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

    private void Solver_OnCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
    {
        // Calculate transform matrix from grabber to world space (Note: if using local space simulation, postmultiply with solver.transform.localToWorldMatrix)
        solver2Grabber = transform.worldToLocalMatrix * solver.transform.localToWorldMatrix;

        // and its inverse:
        grabber2Solver = solver2Grabber.inverse;

        collisionEvent = e;
    }

    private void UpdateParticleProperties()
    {
        // Update rest shape matching of all grabbed softbodies:
        foreach (ObiActor actor in grabbedActors)
        {
            actor.UpdateParticleProperties();
        }
    }

    /**
     * Creates and stores a GrabbedParticle from the particle at the given index.
     * Returns true if we sucessfully grabbed a particle, false if the particle was already grabbed.
     */
    private bool GrabParticle(int index)
    {
        GrabbedParticle p = new GrabbedParticle(index, solver.invMasses[index]);

        // in case this particle has not been grabbed yet:
        if (!grabbedParticles.Contains(p))
        {
            // record the particle's position relative to the grabber, and store it.
            p.localPosition = solver2Grabber.MultiplyPoint3x4(solver.positions[index]);
            grabbedParticles.Add(p);

            // Set inv mass and velocity to zero:
            solver.invMasses[index] = 0;
            solver.velocities[index] = Vector4.zero;

            return true;
        }
        return false;
    }

    /**
     * Grabs all particles currently touching the grabber.
     */
    public void Grab()
    {
        grabbedActors.Clear();
        var world = ObiColliderWorld.GetInstance();

        if (solver != null && collisionEvent != null)
        {
            foreach (Oni.Contact contact in collisionEvent.contacts)
            {
                // this one is an actual collision:
                if (contact.distance < 0.01f)
                {
                    var contactCollider = world.colliderHandles[contact.other].owner;

                    // if the current contact references our collider, proceed to grab the particle.
                    if (contactCollider == localCollider)
                    {
                        // try to grab the particle, if not already grabbed.
                        if (GrabParticle(contact.particle))
                            grabbedActors.Add(solver.particleToActor[contact.particle].actor);
                    }

                }
            }
        }

        UpdateParticleProperties();
    }

    /**
     * Releases all currently grabbed particles. This boils down to simply resetting their invMass.
     */
    public void Release()
    {
        // Restore the inverse mass of all grabbed particles, so dynamics affect them.
        foreach (GrabbedParticle p in grabbedParticles)
            solver.invMasses[p.index] = p.invMass;

        UpdateParticleProperties();
        grabbedActors.Clear();
        grabbedParticles.Clear();
    }

    /**
     * Updates the position of the grabbed particles.
     */
    private void FixedUpdate()
    {
        foreach (GrabbedParticle p in grabbedParticles)
            solver.positions[p.index] = grabber2Solver.MultiplyPoint3x4(p.localPosition);
    }

}
Reply
#5
Dedo arriba 
Thanks works great
Reply
#6
(23-09-2020, 03:17 PM)josemendez Wrote: Here's a sample script (for Obi 5.6) using the particle API and contact callbacks to grab any particles in contact with a collider/trigger.

Simply add it to a collider, and drag/drop the solver reference onto it. Then you can call its Grab() and Release() methods to grab any particles in contact with that collider, and release them.

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

/**
* Sample component that makes a collider "grab" any particle it touches (regardless of which Actor it belongs to).
*/
[RequireComponent(typeof(ObiCollider))]
public class ObiContactGrabber : MonoBehaviour
{

    public ObiSolver solver;

    /**
     * Helper class that stores the index of a particle in the solver, its position in the grabber's local space, and its inverse mass previous to being grabbed.
     * This makes it easy to tell if a particle has been grabbed, update its position while grabbing, and restore its mass after being released.
     */
    private class GrabbedParticle : IEqualityComparer<GrabbedParticle>
    {
        public int index;
        public float invMass;
        public Vector3 localPosition;

        public GrabbedParticle(int index, float invMass)
        {
            this.index = index;
            this.invMass = invMass;
        }

        public bool Equals(GrabbedParticle x, GrabbedParticle y)
        {
            return x.index == y.index;
        }

        public int GetHashCode(GrabbedParticle obj)
        {
            return index;
        }
    }

    private ObiSolver.ObiCollisionEventArgs collisionEvent;                                  /**< store the current collision event*/
    private ObiCollider localCollider;                                                           /**< the collider on this gameObject.*/
    private HashSet<GrabbedParticle> grabbedParticles = new HashSet<GrabbedParticle>();          /**< set to store all currently grabbed particles.*/
    private HashSet<ObiActor> grabbedActors = new HashSet<ObiActor>();                 /**< set of softbodies grabbed during this step.*/

    private Matrix4x4 grabber2Solver;
    private Matrix4x4 solver2Grabber;

    private void Awake()
    {
        localCollider = GetComponent<ObiCollider>();
    }

    private void OnEnable()
    {
        if (solver != null)
            solver.OnCollision += Solver_OnCollision;
    }

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

    private void Solver_OnCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
    {
        // Calculate transform matrix from grabber to world space (Note: if using local space simulation, postmultiply with solver.transform.localToWorldMatrix)
        solver2Grabber = transform.worldToLocalMatrix * solver.transform.localToWorldMatrix;

        // and its inverse:
        grabber2Solver = solver2Grabber.inverse;

        collisionEvent = e;
    }

    private void UpdateParticleProperties()
    {
        // Update rest shape matching of all grabbed softbodies:
        foreach (ObiActor actor in grabbedActors)
        {
            actor.UpdateParticleProperties();
        }
    }

    /**
     * Creates and stores a GrabbedParticle from the particle at the given index.
     * Returns true if we sucessfully grabbed a particle, false if the particle was already grabbed.
     */
    private bool GrabParticle(int index)
    {
        GrabbedParticle p = new GrabbedParticle(index, solver.invMasses[index]);

        // in case this particle has not been grabbed yet:
        if (!grabbedParticles.Contains(p))
        {
            // record the particle's position relative to the grabber, and store it.
            p.localPosition = solver2Grabber.MultiplyPoint3x4(solver.positions[index]);
            grabbedParticles.Add(p);

            // Set inv mass and velocity to zero:
            solver.invMasses[index] = 0;
            solver.velocities[index] = Vector4.zero;

            return true;
        }
        return false;
    }

    /**
     * Grabs all particles currently touching the grabber.
     */
    public void Grab()
    {
        grabbedActors.Clear();
        var world = ObiColliderWorld.GetInstance();

        if (solver != null && collisionEvent != null)
        {
            foreach (Oni.Contact contact in collisionEvent.contacts)
            {
                // this one is an actual collision:
                if (contact.distance < 0.01f)
                {
                    var contactCollider = world.colliderHandles[contact.other].owner;

                    // if the current contact references our collider, proceed to grab the particle.
                    if (contactCollider == localCollider)
                    {
                        // try to grab the particle, if not already grabbed.
                        if (GrabParticle(contact.particle))
                            grabbedActors.Add(solver.particleToActor[contact.particle].actor);
                    }

                }
            }
        }

        UpdateParticleProperties();
    }

    /**
     * Releases all currently grabbed particles. This boils down to simply resetting their invMass.
     */
    public void Release()
    {
        // Restore the inverse mass of all grabbed particles, so dynamics affect them.
        foreach (GrabbedParticle p in grabbedParticles)
            solver.invMasses[p.index] = p.invMass;

        UpdateParticleProperties();
        grabbedActors.Clear();
        grabbedParticles.Clear();
    }

    /**
     * Updates the position of the grabbed particles.
     */
    private void FixedUpdate()
    {
        foreach (GrabbedParticle p in grabbedParticles)
            solver.positions[p.index] = grabber2Solver.MultiplyPoint3x4(p.localPosition);
    }

}

I;m getting the following error using this code with Obi Rope 5.3

Error CS0103 The name 'ObiColliderWorld' does not exist in the current context

Is this normal?
Reply
#7
(02-12-2020, 04:08 AM)zig420 Wrote:
I;m getting the following error using this code with Obi Rope 5.3

Error CS0103 The name 'ObiColliderWorld' does not exist in the current context

Is this normal?

Quoting the post:

Quote:Here's a sample script (for Obi 5.6)

You need 5.6 or up to use this script. In older versions, ObiColliderWorld did not exist, colliders were accessed in a completely different way.
Reply
#8
(02-12-2020, 08:37 AM)josemendez Wrote: Quoting the post:


You need 5.6 or up to use this script. In older versions, ObiColliderWorld did not exist, colliders were accessed in a completely different way.

Much appreciated. I upgraded to 5.6 and there are no more errors. Now in terms of implementation, where do I call the Grab() and Release() functions?
Reply
#9
(02-12-2020, 09:55 PM)zig420 Wrote: Much appreciated. I upgraded to 5.6 and there are no more errors. Now in terms of implementation, where do I call the Grab() and Release() functions?

That’s completely up to you and how you want to implement iteraction/grabbing. For instance, calling these methods in response to user input (button press/release) is a typical use case.
Reply
#10
(03-12-2020, 12:57 AM)josemendez Wrote: That’s completely up to you and how you want to implement iteraction/grabbing. For instance, calling these methods in response to user input (button press/release) is a typical use case.

I've been trying to get this working for weeks. It would be nice to have a clear tutorial or steps on how to get this working.
Reply