Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
scripting ropes with pin constraints & tethers
#1
Hi –

I’m working on a scene made up of hanging chains of objects, each connected by a short rope: the first particle of each rope is pinned to an object at the top, and the last particle is pinned to an object at the bottom. The first object of the first rope is set to kinematic (to anchor the whole chain at the top). It’s all working pretty well, but I have a few questions.

The final scene will have lots of ropes; each chain contains about 6 ropes, and there will be more than 20 chains in total, so that means more than 100 ropes. Putting all of this together in the editor was impractical, so I’ve written a script (based on ObiRopeHelper.cs) to help automate the process. Script included below.

When the simulation starts, the rope springs like a rubber band that’s just been let go and then settles down. I'd like to have everything begin in a resting state (unmoving). In this thread: http://obi.virtualmethodstudio.com/forum...hp?tid=175 the solution suggested is disable the rope and wait for the next FixedUpdate before reenabling. I’ve tried to implement that in my script, but it doesn’t seem to be having any effect.

Next, I would like the ropes to be as non-elastic as possible. I realize that the recommended way of doing this is via tethers, but because my ropes aren’t fixed, tethers wouldn’t seem to be an option. However, in this thread: http://obi.virtualmethodstudio.com/forum....php?tid=6 there’s a method described in which the rope is fixed, tethers are generated, then the rope is unfixed. Once again, I’ve tried to implement this in my script, but it doesn’t seem to be having any effect.

I’ve therefore tried to set the rope length by using reduced values for stretchingScale (in ObiDistanceConstraints). I’m sure this isn’t ideal, but it basically seems to work (but maybe this is causing the initial rubber band behavior?)

Finally, I’ve found that each individual rope has to have its own dedicated solver; if I try to share a single solver across multiple ropes, Unity immediately crashes. I’m guessing this has something to do with removing and adding multiple pin constraints from the solver simultaneously/asynchronously? (Just a guess). Anyway, my question is whether there will be any problems (like a performance hit) by having upwards of 100 solvers in a single scene.


Code:
using UnityEngine;
using System.Collections;

namespace Obi {
    [RequireComponent(typeof(ObiRope))]
    [RequireComponent(typeof(ObiCatmullRomCurve))]
    [RequireComponent(typeof(ObiPinConstraints))]
    [RequireComponent(typeof(ObiDistanceConstraints))]
    [RequireComponent(typeof(ObiTetherConstraints))]

    public class ObiConnectorRope : MonoBehaviour {

        public ObiSolver solver;
        public ObiActor actor;
        
        public ObiRopeSection section;
        public Material material;
        
        public float ropeLength = 1.0f; // desired rope length
        public float uvYMult = 2.5f; // keep UVScale Y consistent across rope lengths
        
        public ObiCollider upperPinObject;
        public ObiCollider lowerPinObject;
        
        private Transform point1; // offset at top of object
        private Transform point2; // offset at bottom of object
        
        private ObiRope rope;
        private ObiCatmullRomCurve path;
        
        private ObiPinConstraints pinConstraints;
        private ObiPinConstraintBatch constraintsBatch;
        private ObiDistanceConstraints distanceConstraints;
    
        void Start () {
            
            actor.enabled = false;
            
            // move rope position to match upper pinned object
            transform.position = upperPinObject.transform.position;
            
            // Get all needed components and interconnect them:
            rope = GetComponent<ObiRope>();
            path = GetComponent<ObiCatmullRomCurve>();
            rope.Solver = solver;
            rope.ropePath = path;    
            rope.Section = section;
            GetComponent<MeshRenderer>().material = material;
            
            // use stretching scale to set approximate rope length & UVScale Y
            distanceConstraints = rope.GetComponent<ObiDistanceConstraints>();
            distanceConstraints.stretchingScale = (ropeLength * 0.14f) - 0.05f; // ad hoc formula
            rope.UVScale = new Vector2(1.0f, ropeLength * uvYMult);
            
            // register offsets
            point1 = upperPinObject.transform.Find("pinPointB").gameObject.transform;
            point2 = lowerPinObject.transform.Find("pinPointA").gameObject.transform;
            
            // Calculate rope start/end and direction in local space:
            Vector3 localStart = transform.InverseTransformPoint(upperPinObject.transform.position);
            Vector3 localEnd = transform.InverseTransformPoint(lowerPinObject.transform.position);
            Vector3 direction = (localEnd-localStart).normalized;
            // Add control points
            path.controlPoints.Add(localStart-direction);
            path.controlPoints.Add(localStart);
            path.controlPoints.Add(localEnd);
            path.controlPoints.Add(localEnd+direction);

            // Setup the simulation:
            StartCoroutine(Setup());
            
            // Wait for next FixedUpdate
            StartCoroutine(WaitOne());
            
            actor.enabled = true;
        }

        IEnumerator Setup(){
            
            // Generate particles and add them to solver:        
            yield return StartCoroutine(rope.GeneratePhysicRepresentationForMesh());
            
            rope.AddToSolver(null);
            
            // Generate tethers
            // Fix first particle:
            float tempMass = rope.invMasses[0];
            rope.invMasses[0] = 0;
            Oni.SetParticleInverseMasses(solver.OniSolver,new float[]{0},1,rope.particleIndices[0]);
            rope.GenerateTethers(ObiActor.TetherType.AnchorToFixed);
            // Unfix first particle:
            rope.invMasses[0] = tempMass;
            Oni.SetParticleInverseMasses(solver.OniSolver,new float[]{0},1,rope.particleIndices[0]);
            actor.PushDataToSolver(ParticleData.INV_MASSES);
            
            // Set pin constraints    
            pinConstraints = rope.GetComponent<ObiPinConstraints> ();
            constraintsBatch = (ObiPinConstraintBatch)pinConstraints.GetBatches() [0];
            pinConstraints.RemoveFromSolver(null);    
            // Add top pin constraint
            constraintsBatch.AddConstraint(rope.particleIndices[rope.particleIndices[0]], upperPinObject.GetComponent<ObiCollider>(), point1.localPosition, 0f);
            // Add bottom pin constraint
            constraintsBatch.AddConstraint(rope.particleIndices[rope.UsedParticles-1], lowerPinObject.GetComponent<ObiCollider>(), point2.localPosition, 0f);

            pinConstraints.AddToSolver(null);
            
            pinConstraints.PushDataToSolver();
        }    
        
        IEnumerator WaitOne(){
            yield return new WaitForFixedUpdate();
        }    
    }
}
Reply


Messages In This Thread
scripting ropes with pin constraints & tethers - by phoberman - 26-12-2017, 07:43 PM