Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Rope resolution inconsistency resulting in larger forces required
#1
I have two ropes defined.
Each has a different starting shape. Because they have a different initial shape, they must have different blueprints. 
They both have the same settings in the blueprint though, in particular, they both have a resolution = 0.206
(See attached initial state image)
   

During runtime, I use a RopeCursor to increase the length of both ropes to ~20.0.
I use a character to drag the ends around and stretch them out.

The issue is... 
The character can successfully drag one of the ropes, but not the other.
This leads to an inconsistent gameplay experience based upon the starting state of the rope.

To debug the issue, I attached a particle renderer and discovered that the rope that cannot be dragged has significantly more particles than the other.
(See attached runtime image)
   
The rope with red particles cannot be dragged by my character (the force required is too great).
The green particle rope behaves well.

I suspect that the fact the red rope has more particles is why they require more force to move?

Some questions arise:
 - Why does the red particle rope have more particles? (I suspect because I required more control points in the initial shape to create the starting coil shape)
 - Is there a way that I can make sure both ropes have the same inter-particle distance regardless of the starting shape?
 - Alternatively, is there a way to make sure the force required to move each rope is consistent? (lower the friction on the red particles somehow?)

Thanks for your assistance
Reply
#2
(17-03-2024, 05:25 AM)inbilla Wrote: Some questions arise:
 - Why does the red particle rope have more particles? (I suspect because I required more control points in the initial shape to create the starting coil shape)

Correct, there’s always at least one particle inserted at each control point’s location to ensure the shape defined by the path can be achieved by the rope. A tight coil such as the one in your screenshot is simply impossible to achieve while keeping a large inter-particle distance, like that of your other rope. As a result, the coiled rope has a significantly smaller average inter-particle distance, which the cursor tries to keep while extending the rope (otherwise you’d have sections of different effective resolution in the same rope, which is not good!)

(17-03-2024, 05:25 AM)inbilla Wrote:  - Is there a way that I can make sure both ropes have the same inter-particle distance regardless of the starting shape?

You can increase the straight rope’s resolution so that both have similar distancing between particles, but in the general case this isn’t something you can do: imagine a rope of resolution zero (only 2 particles, one at each end) but you want it to have a curved shape, that would be asking the system to bend a straight line which is mathematically impossible.

(17-03-2024, 05:25 AM)inbilla Wrote:  - Alternatively, is there a way to make sure the force required to move each rope is consistent? (lower the friction on the red particles somehow?)

Friction doesn’t play any role in this: ropes with more particles are harder to move simply because they have more mass, and you need larger forces to move heavier objects because F=ma.

A simple solution exists in this case though: say you need two ropes of different lenghts/resolution to have the exact same mass: divide their total mass by the amount of particles in each, and assign the resulting mass to each individual particle. See:
https://obi.virtualmethodstudio.com/manu...icles.html

Kind regards,
Reply
#3
Ok, thanks Jose.
I will attempt to adjust the mass of all the particles after every call I make to "ChangeLength".

Another question:
 - I really do not need to resolution to be so high once the rope is uncoiled (the resolution of the green particle rope is fantastic for my situation, and obviously will be more performant). Is it possible for me to reduce the resolution of the rope at runtime? (I really hope so?)
Reply
#4
(19-03-2024, 06:28 AM)inbilla Wrote:  - I really do not need to resolution to be so high once the rope is uncoiled (the resolution of the green particle rope is fantastic for my situation, and obviously will be more performant). Is it possible for me to reduce the resolution of the rope at runtime? (I really hope so?)

Unfortunately not, you can't change the resolution of a rope at runtime. The reason is that resolution is a property of the blueprint (essentially a spline), not the rope itself.

You could build a second blueprint by resampling the rope particles as control points, reduce the resolution on this blueprint and then create a lower resolution rope from it at runtime, but it's quite involved and I doubt the results would look any good.

kind regards,
Reply
#5
Ideally, I would like to be able to define my ropes to have a consistent interparticle distance, regardless of length and regardless of starting shape.
Being able to adjust this dynamically would be ideal, but perhaps there are other ways?

Can I create a blueprint at runtime?

I'm open to reducing the resolution dynamically... what do you think will happen when you say "I doubt the results will look any good"?

Ideally I'd have a min/max IPD defined, and then adjust in between.
I really need the behaviour and performance footprint of the ropes to become consistent (for a given length) regardless of starting shape...

Reply
#6
(19-03-2024, 07:31 AM)inbilla Wrote: Ideally, I would like to be able to define my ropes to have a consistent interparticle distance, regardless of length and regardless of starting shape.
Being able to adjust this dynamically would be ideal, but perhaps there are other ways?

Resolution is intended to provide consistent inter particle distance regardless of length - as it essentially controls amount of particles per length unit.

However if you cram your control points closer than the inter-particle distance determined by resolution, you need at least one particle per control point for the rope to resemble the shape defined by the points: it is geometrically impossible to keep an inter-particle distance that's larger than the distance between control points without completely breaking the shape of the rope.

Intuitively, this is very similar to Nyquist-Shannon's sampling theorem in signal processing: it states that in order to "catch" the shape of a wave using discrete samples, you need to sample at least at twice the frequency of the wave. This means if the wave goes up and then down, to reconstruct its shape you need at least a sample at the "up", a sample in the "middle", and a sample at the "down". Otherwise when you join the dots/samples you'll get an unrecognizable shape.

In this case, the wave is the blueprint's path, and the discrete samples are your particles. If you add less particles than control points, the shape of the rope will not even resemble the blueprint.

(19-03-2024, 07:31 AM)inbilla Wrote: Can I create a blueprint at runtime?

Yes, it's just a regular asset. See: http://obi.virtualmethodstudio.com/manua...ctors.html
There's also several sample scenes included with Obi that do this (RopeGrapplingHook, RopeNet just to name a couple)

(19-03-2024, 07:31 AM)inbilla Wrote: I'm open to reducing the resolution dynamically... what do you think will happen when you say "I doubt the results will look any good"?

I mean that there will be both a hitch in performance and visuals due to A) resampling and blueprint generation taking a lot of time and B) the rope suddenly using far less particles.

(19-03-2024, 07:31 AM)inbilla Wrote: I really need the behaviour and performance footprint of the ropes to become consistent (for a given length) regardless of starting shape...

As stated before, this is simply impossible in the general case. Thinking of extremes should make it obvious: imagine a rope that must be coiled around a toothpick (1mm in diameter), now add the requirement that the inter-particle distance must be 10 cm, while keeping the coil shape as closely as possible: if the rope can only change direction every 10 cm, there's no way for it to coil around a 1mm thick stick and not look like a garbled mess.

kind regards,
Reply
#7
Thanks for your response. I agree with your statements about the initial coil and that requires more particles. I don't want that to have less particles. Generally I don't believe I'm asking for a solution that violates those core geometric realities. 

The tight coil is only relevant as an initial state. When coiled, it is quite short. I am happy for the rope to have more particles whilst it is this length and this shape. It becomes much longer as the rope is pulled from that state (I change the length - attempting to immitate unrolling of the rope). 

Ideally I could slowly tweak the IPD as the length of the rope increases. 

Perhaps you might know how I might go about adjusting the IPD whenever I add new particles with the Change length method?
Reply
#8
(19-03-2024, 09:00 AM)inbilla Wrote: Ideally I could slowly tweak the IPD as the length of the rope increases. 
Perhaps you might know how I might go about adjusting the IPD whenever I add new particles with the Change length method?

You could modify ObiRopeBlueprintBase by adding a setter to the interParticleDistance property, then manually set it as you use the cursor.

Note this will only affect new particles added by the cursor, and will have no effect on already active particles. You will need to use distance constraint's "stretching scale" parameter (accessible from the ObiRope component) to control the relative distance between active particles that are already part of the simulation.

kind regards,
Reply
#9
Hi Jose,
I thought I'd return and offer my experience trying to implement this.
I think I've managed to acheive the behaviour I was looking for thanks to your pointers above Sonrisa . 
I'm using the following logic:

Code:
private void ChangeVisualRopeLength(float newLength)
{
    if (!rope.isLoaded)
        return;

    // Current IPD is? (Ignore the start/end, they have pinned particles
    var endElementRestLength = rope.elements[0].restLength;
    endElementRestLength += rope.elements[rope.elements.Count - 1].restLength;
    var targetOtherElementLength = newLength - endElementRestLength;

    // Calculate the new IPD
    var currentIPD = rope.ropeBlueprint.interParticleDistance;
    var maxIPD = Mathf.Max(maxDesiredIPD, currentIPD);
    var stretchIPD = targetOtherElementLength / (rope.elements.Count - 2); // Don't touch the first and last elements
    stretchIPD = Mathf.Clamp(stretchIPD, 0, maxDesiredIPD);

    // Iterate trough all distance elements and update their rest lengths.
    var solver = rope.solver;
    var newElementRestLength = stretchIPD;
    rope.ropeBlueprint.interParticleDistance = newElementRestLength;
    int elementCount = rope.elements.Count;
    for (int i = 0; i < elementCount; ++i)
    {
        if (i == 0 || i == elementCount - 1)
        {
            // don't change rest lengths of first and last elements
            continue;
        }
        // Never shorten an element in this way (delete them instead)
        rope.elements[i].restLength = Mathf.Max(newElementRestLength, rope.elements[i].restLength);
    }
    // Update the mass of particles across the rope (except the ends)
    float totalActiveMass = newLength * massPerMeter;
    float massPerParticle = totalActiveMass / rope.particleCount;
    for (int i = 0; i < rope.particleCount; ++i)
    {
        if (i < 2 || i > rope.particleCount - 2)
        {
            // don't change rest lengths of first and last elements
            continue;
        }
        var solverIndex = rope.solverIndices[i];
        solver.invMasses[solverIndex] = ObiUtils.MassToInvMass(massPerParticle);
    }

    // Recalculate rest length as the cached value is used during ChangeLength
    rope.RecalculateRestLength();

    // Finally, tell ObiCursor to add/remove particles as required
    ropeCursor.ChangeLength(newLength);
}

Then I'm increasing the length whenever the real length deviates too far from the intended rest length:
Code:
private void FixedUpdate()
{
    float actualLength = rope.CalculateLength();
    float restLength = rope.restLength;
    if (actualLength - restLength > stretchThreshold)
    {
        if (restLength < maxLength)
        {
            float newLength = Mathf.Min(maxLength, actualLength + extensionRate * Time.fixedDeltaTime);
            ChangeVisualHoseLength(newLength);
        }
    }
}

Thanks for your help!
I'd be interested in any feedback you have on the above code if you observe any obvious problems.
Again, thanks for your help.
Reply