Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
ObiColliders and Procedural Load
#8
(03-02-2018, 04:13 AM)jonworks Wrote: Wow! This is really great. Thank you for doing this. It has transformed the difficulty of using this asset for me.

Have you thought about putting a simple rope generator in a static function? 

So I've got the rope generating and something hanging from it, but I'm having the same problem as a lot of other people again - the rope is unusably elastic like a rubber band. In your comments you wrote:

/// Further explanation: there's no way to correlate iteration count or weight to length in order to maintain constant stiffness (not in Obi, nor any existing engine). You could create a single
/// tether constraint joining the first and last particle in the rope, and set its length to be rope.RestLenght. This is a common workaround in many physics engines.

I realize it's not possible to have perfectly uniform and predictable elasticity of the rope with this asset, however it has to be possible and it's absolutely critical to get it somewhere in the ballpark of not behaving like a rubber band. Will the settings for making this happen always be the same (and if so why isn't the rope configured like that by default) or will they vary depending on the length and attached mass of the rope? If they will vary depending on the length and attached mass then there must be some algorithm that can evaluate those variables and output at least approximate settings.

It might be necessary to derive that algorithm through trial and error, which is what I've been trying to do. But this is what I've run into.

The three factors I've found that play into this are rope mass (INV_MASSES), distance constraint iterations, and tethers.

By adjusting the distance constraint iterations while the application is running in editor, I have not been able to observe any difference in rope behavior. I ran into the same situation when I tried adding a pin constraint while the application is running. Are these things that can only be done when the application is stopped?

I tried adding tethers but I can't seem to make anything happen. Does nothing:
        tether_constraints = rope.GetComponent<ObiTetherConstraints>();
        tether_constraints.stiffness = .99f;
        tether_constraints.tetherScale = .99f;
        tether_constraints.PushDataToSolver();
This changes the values displayed in inspector but does not change the behavior of the rope.

Also does nothing:
rope
.GenerateTethers(ObiActor.TetherType.Hierarchical);


I can click generate tethers in inspector while the application is running, and the behavior of the rope changes (although it looks like it won't be a complete solution) - and also whenever I change the length of the rope with cursor.ChangeLength the tethers get completely messed up.

Thanks,

Jon

Hi Jon,

There is no general way to get 100% non-stretchy chain of constraints in Obi, or in any existing engine, unless you're willing to accept some limitations. It's just the way physics engines (specially iterative ones) work. If you need inextensibility in the general case, your only options are:

- Reduce the physics timestep (Edit->Project settings->Time->Fixed timestep)
- Increase the amount of solver iterations.

Both the issue and the solutions are common to all engines, not just Obi: PhysX, Box2D, Bullet, Newton, etc. Try setting Unity's fixed timestep to 0.01 and the amount of distance constraint iterations to 15, for relatively short ropes this will work ok, unless your mass ratios are extremely large (very heavy object hanging from a very light rope) or your rope has lots of constraints. Both settings can be applied at any time both in editor and play mode, and will have immediate effect on the rope behavior, making it less elastic.

The right values depend a lot on the particular situation you're trying to simulate. Longer ropes (ropes that have many constraints), ropes dealing with large mass ratios, and ropes dealing with high speeds will all need more iterations and/or shorter timesteps to look inextensible. The stiffness of the rope does not increase linearly with the amount of iterations, also depends on the timestep and the maximum speed, the mass ratios, etc. In fact, only very recently a way to get stiffness working in a semi-physical way with position-based engines was discovered (XPBD, which is implemented in Obi since 3.0): http://mmacklin.com/xpbd.pdf. Still, the minimum amount of iterations needed to achieve a certain stiffness given the timestep, the constraints graph and the maximum mass ratio is just not possible to calculate. So your best bet is to crank up the amount of iterations and/or reduce the timestep until the worst possible case in your game works acceptably well.

The default parameters provided by Obi are meant for acceptable quality in short ropes, with a minimum performance hit. Most people want things to run as fast as possible out of the box, then tweak them if they don't fit their particular use case. Obi is meant for advanced users, so knowledge about basic concepts like timestep and convergence is assumed (since they are common to all engines and absolutely needed to be able to use any of them effectively for any non-trivial purpose), as it is that most users will immediately resort to tweak Unity's timestep and the solver iteration count if the simulation is too stretchy.

Regarding your tethers code:

- Setting tether parameters and pushing them to the solver will have no effect at all if no tether constraints have been generated (for obvious reasons). Also setting the length/stiffness to be 99% of their default values will have very little, if any, noticeable effect.
- Generating tethers will only have any effect after calling GeneratePhysicalRepresentationForMesh has finished. There's no particles to add tethers to before that.

A good solution for the particular case of a pendulum (or any object hanging from a rope) is outlined in the same comment you quoted:

Quote:You could create a single tether constraint joining the first and last particle in the rope, and set its length to be rope.RestLenght. This is a common workaround in many physics engines.

This approach is extremely simple to implement, routinely used in a lot of games and works well for most situations. Here's a basic implementation using the same sample code:

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

public class RuntimeRopeGenerator
{
    private ObiRope rope;
    private ObiRopeCursor cursor;
    private ObiSolver solver;
    private int pinnedParticle = -1;

    /// <summary>
   /// Creates a straight rope anchored to a transform at the top.
   /// Transform may or may not move around and may or may not have a rigidbody.
   /// When you call this the rope will appear in the scene and immediately interact with gravity and objects with ObiColliders.
   /// Called from anywhere (main thread only)
   /// </summary>
    public IEnumerator MakeRope(Transform anchoredTo, Vector3 attachmentOffset, float ropeLength)
    {
        // create a new GameObject with the required components: a solver, a rope, and a curve.
        // we also throw a cursor in to be able to change its length.
        GameObject ropeObject = new GameObject("rope",typeof(ObiSolver),
                                                      typeof(ObiRope),
                                                      typeof(ObiCatmullRomCurve),
                                                      typeof (ObiRopeCursor));

        // get references to all components:
        rope                     = ropeObject.GetComponent<ObiRope>();
        cursor                     = ropeObject.GetComponent<ObiRopeCursor>();
        solver                     = ropeObject.GetComponent<ObiSolver>();
        ObiCatmullRomCurve path = ropeObject.GetComponent<ObiCatmullRomCurve>();

        // set up component references (see ObiRopeHelper.cs)
        rope.Solver = solver;
        rope.ropePath = path;    
        rope.section = Resources.Load<ObiRopeSection>("DefaultRopeSection");
        cursor.rope = rope;

        // set path control points (duplicate end points, to set curvature as required by CatmullRom splines):
        path.controlPoints.Clear();
        path.controlPoints.Add(Vector3.zero);
        path.controlPoints.Add(Vector3.zero);
        path.controlPoints.Add(Vector3.down*ropeLength);
        path.controlPoints.Add(Vector3.down*ropeLength);

        // parent the rope to the anchor transform:
        rope.transform.SetParent(anchoredTo,false);
        rope.transform.localPosition = attachmentOffset;

        // generate particles/constraints and add them to the solver (see ObiRopeHelper.cs)
        yield return rope.StartCoroutine(rope.GeneratePhysicRepresentationForMesh());
        rope.AddToSolver(null);

        // get the last particle in the rope at its rest state.
        pinnedParticle = rope.UsedParticles-1;

        // add a tethers batch:
        ObiTetherConstraintBatch tetherBatch = new ObiTetherConstraintBatch(true,false,0,1);
        rope.TetherConstraints.AddBatch(tetherBatch);
        UpdateTethers();

        // fix first particle in place (see http://obi.virtualmethodstudio.com/tutorials/scriptingparticles.html)
        rope.invMasses[0] = 0;
        Oni.SetParticleInverseMasses(solver.OniSolver,new float[]{0},1,rope.particleIndices[0]);
    }

    /// <summary>
   /// MakeRope and AddPendulum may NOT be called on the same frame. You must wait for the MakeRope coroutine to finish first, as creating a rope is an asynchronous operation.
   /// Just adds a pendulum to the rope on the un-anchored end.
   /// </summary>
    public void AddPendulum(ObiCollider pendulum, Vector3 attachmentOffset)
    {
        // simply add a new pin constraint (see http://obi.virtualmethodstudio.com/tutorials/scriptingconstraints.html)
        rope.PinConstraints.RemoveFromSolver(null);
        ObiPinConstraintBatch batch = (ObiPinConstraintBatch)rope.PinConstraints.GetBatches()[0];
        batch.AddConstraint(pinnedParticle, pendulum, attachmentOffset, 1);
        rope.PinConstraints.AddToSolver(null);
    }

    /// <summary>
   /// RemovePendulum and AddPendulum may be called on the same frame.
   /// </summary>
    public void RemovePendulum()
    {
        // simply remove all pin constraints (see http://obi.virtualmethodstudio.com/tutorials/scriptingconstraints.html)
        rope.PinConstraints.RemoveFromSolver(null);
        rope.PinConstraints.GetBatches()[0].Clear();
        rope.PinConstraints.AddToSolver(null);
    }

    /// <summary>
    /// Like extending or retracting a winch.
    /// </summary>
    public void ChangeRopeLength(float changeAmount)
    {
        // the cursor will automatically add/remove/modify constraints and particles as needed to obtain the new length.
        cursor.ChangeLength(rope.RestLength + changeAmount);
        UpdateTethers();
    }

    private void UpdateTethers()
    {
        rope.TetherConstraints.RemoveFromSolver(null);
        ObiTetherConstraintBatch batch = (ObiTetherConstraintBatch)rope.TetherConstraints.GetBatches()[0];
        batch.Clear();
        batch.AddConstraint(0,pinnedParticle, rope.RestLength, 1, 1);
        batch.Cook();
        rope.TetherConstraints.AddToSolver(null);
    }
}

As you can see the only additions are the creation of a new tethers batch, and the UpdateTethers() method that re-generates a single tether of the adequate length every time the rope length changes. Note that if your rope is long enough it might still "droop" at low iteration counts even though the pendulum object maintains the proper distance from the attachment point.
Reply


Messages In This Thread
ObiColliders and Procedural Load - by jonworks - 18-01-2018, 07:03 AM
RE: ObiColliders and Procedural Load - by josemendez - 03-02-2018, 05:54 PM