Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
I would like to know the optimized way to catch objects in 'SteamVR' using 'SoftBody'
#2
Hi,

Reparenting a deformable object (not just a softbody) would just not work.  Not only because it would no longer be inside the solver, but because determining the rotation/position/scale of a deformable object using a single transform is just not possible, so even if the softbody could be simulated outside of a solver, parenting it to a transform would not "grab" it. This question has come up in the forums tens of times, usually in the form of "can I scale a softbody?", "can I set the position of a cloth?" or "can I change the length of a rope by scaling it"? The answer to all these is the same: a transform is a single 4x4 matrix, and you can't determine the size, shape, orientation and position of a deformable object with just one matrix.

A softbody is a collection of rigid particles, each one with its own position and orientation, held together by constraints. So to pick up the softbody, you need to pick up one (or a few) of its particles.

Easiest way to do this is to detect which particles are in contact with a collider/trigger, then fix them by setting their mass to infinite and set their position relative to the collider. This is what the sample script ObiColliderGrabber does (included in 5.2 and up): it will detect all particles touching a collider, then "grab" them when you call its Grab() method.

I'm pasting the code here, in case you have 5.0 or 5.1. Should work there too:

Code:
using UnityEngine;
using System.Collections;
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 ObiColliderGrabber : 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 Obi.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<ObiSoftbody> grabbedSoftbodies = new HashSet<ObiSoftbody>();                 /**< set of softbodies grabbed during this step.*/
    private Matrix4x4 grabber2Solver;
    private Matrix4x4 solver2Grabber;

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

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

    private void OnDisable()
    {
        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 UpdateRestShapeMatching()
    {
        // Update rest shape matching of all grabbed softbodies:
        foreach (ObiSoftbody softbody in grabbedSoftbodies)
        {
            var constraints = softbody.GetConstraintsByType(Oni.ConstraintType.ShapeMatching);
            if (constraints != null)
            {
                var batches = constraints.GetBatchInterfaces();
                foreach (ObiShapeMatchingConstraintsBatch batch in batches)
                    Oni.CalculateRestShapeMatching(solver.OniSolver, batch.oniBatch);
            }
        }
    }

    /**
     * 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()
    {
        grabbedSoftbodies.Clear();

        foreach (Oni.Contact contact in collisionEvent.contacts)
        {
            // this one is an actual collision:
            if (contact.distance < 0.01f)
            {
                Component contactCollider;
                if (ObiCollider.idToCollider.TryGetValue(contact.other, out contactCollider))
                {
                    // if the current contact references our collider, proceed to grab the particle.
                    if (contactCollider == localCollider.SourceCollider)
                    {
                        // try to grab the particle, if not already grabbed.
                        if (GrabParticle(contact.particle))
                        {
                            // we want to know if we grabbed a softbody, to update its rest shape.
                            var softbody = solver.particleToActor[contact.particle].actor as ObiSoftbody;
                            if (softbody != null)
                                grabbedSoftbodies.Add(softbody);
                        }
                    }
                }
            }
        }

        UpdateRestShapeMatching();
    }

    /**
     * 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;

        grabbedParticles.Clear();

        // Also update rest shape matching:
        UpdateRestShapeMatching();
        grabbedSoftbodies.Clear();
    }

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

    /**
     * Just for convenience. Ideally, this should not be part of this component.
     * You're expected to control the Grabber from outside.
     */
    public void Update()
    {
        if (Input.GetKeyDown(KeyCode.G))
        {
            Grab();
        }

        if (Input.GetKeyDown(KeyCode.R))
        {
            Release();
        }
           
    }
}
Reply


Messages In This Thread
RE: I would like to know the optimized way to catch objects in 'SteamVR' using 'SoftBody' - by josemendez - 17-06-2020, 08:12 AM