Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Copy ropes in play mode
#1
Greetings. I have not found a normal and stable way to copy ropes. I need to have two ropes in the same solver, one of them is always on. When I turn off one and turn on the other, I need to copy the position and number of particles. Visually it should look like one rope is replaced by another, but remains in the same state as the previous one. I don't want to use the ObiParticleRenderer solution, I want to copy all positions of the previous rope to the one we are going to turn on. As far as I know, the positions are accessed via solver.positions, but if I understand correctly, these are the positions of both ropes being simulated.

Code:
public void AttachRope()
        {
            _blueprint = ScriptableObject.CreateInstance<ObiRopeBlueprint>();
            var fatherConnectionPoint = _playersContainer[PlayerCharacter.Father].ConnectionPoint;
            var sonConnectionPoint = _playersContainer[PlayerCharacter.Son].ConnectionPoint;
            Vector3 father = ObiRope.transform.InverseTransformPoint(fatherConnectionPoint.position);
            Vector3 son = ObiRope.transform.InverseTransformPoint(sonConnectionPoint.position);
            int allLayers = (1 << 16) - 1;
            int excludeLayer15 = ~(1 << 15);
            int finalMask = allLayers & excludeLayer15;

            int filter = ObiUtils.MakeFilter(finalMask, 1);

            _blueprint.path.Clear();
            _blueprint.path.AddControlPoint(father, -father.normalized, father.normalized, Vector3.zero, 0.1f, 0.1f, 1, filter, Color.white, "start");
            _blueprint.path.AddControlPoint(son, -son.normalized, son.normalized, Vector3.zero, 0.1f, 0.1f, 1, filter, Color.white, "end");
            _blueprint.path.FlushEvents();
            _blueprint.Generate();
            ObiRope.ropeBlueprint = _blueprint;

            var pinConstraints = ObiRope.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
            pinConstraints.Clear();
            var batch = new ObiPinConstraintsBatch();
            batch.AddConstraint(ObiRope.solverIndices[0], _fatherConnectionPointObiCollider, Vector3.zero, Quaternion.identity, 0, 0, float.PositiveInfinity);
            batch.AddConstraint(ObiRope.solverIndices[_blueprint.activeParticleCount - 1], _sonConnectionPointObiCollider, Vector3.zero, Quaternion.identity, 0, 0, float.PositiveInfinity);
            batch.activeConstraintCount = 2;
            pinConstraints.AddBatch(batch);
            ObiRope.SetConstraintsDirty(Oni.ConstraintType.Pin);
        }
Here's the code I use to create a rope between father and son in real time. It is created in a straight line from son to father. I also have a second rope with a different physical behavior that gravitates to points of interest in zero gravity. At some point I turn off the zero gravity thread and load other settings into solver, while turning on the second thread. I need in some cases for the thread with gravity to replicate the thread without gravity, but at the same time, sometimes I need it the way it already works.
Reply
#2
(08-01-2024, 03:53 PM)Alexander34 Wrote: I want to copy all positions of the previous rope to the one we are going to turn on. As far as I know, the positions are accessed via solver.positions, but if I understand correctly, these are the positions of both ropes being simulated.

Hi!

Assuming both ropes have the same amount of particles, it's just a matter of iterating trough their elements copying particle positions as you go. Each element points to the correct indices in solver.positions for the current rope. See: http://obi.virtualmethodstudio.com/manua...ropes.html

If they have different amounts of particles, then things get considerably more complex as you need to decide whether to remove excess particles from one rope, add extra particles to the other one, where to remove/add particles from/to, connect them using new constraints, etc. Most of this can be handled automatically using a ObiRopeCursor, however it works in terms of length - instead of particles - so it would also require some non trivial work.

(08-01-2024, 03:53 PM)Alexander34 Wrote: I don't want to use the ObiParticleRenderer solution,

Not sure what you mean, sorry. All ObiParticleRenderer does is render the particles so that you can visualize them: it doesn't copy particles, duplicate the rope or anything similar?
Reply
#3
(16-01-2024, 08:41 AM)josemendez Wrote: Hi!

Assuming both ropes have the same amount of particles, it's just a matter of iterating trough their elements copying particle positions as you go. Each element points to the correct indices in solver.positions for the current rope. See: http://obi.virtualmethodstudio.com/manua...ropes.html

If they have different amounts of particles, then things get considerably more complex as you need to decide whether to remove excess particles from one rope, add extra particles to the other one, where to remove/add particles from/to, connect them using new constraints, etc. Most of this can be handled automatically using a ObiRopeCursor, however it works in terms of length - instead of particles - so it would also require some non trivial work.


Not sure what you mean, sorry. All ObiParticleRenderer does is render the particles so that you can visualize them: it doesn't copy particles, duplicate the rope or anything similar?
I was trying to anticipate the answer and said that you can get what you want if you just change the material in ObiParticleRenderer, so we would kind of change the ropes visually, but their background would stay the same. However, I happen to have two ObiRope, and so far this way excludes risks related to future requirements. Yes, I already guessed that it is possible to copy their positions into the new obiRope before disconnecting the old obiRope. However, if I understand correctly we get access to the particles from solver.positions, and there may be positions of particles of other ropes of solver. I need the positions of a separate obiRope. And yes, you are right, I need to add the missing particles to the front or back of the rope. ObiRopeCursor as far as I know only works in rantime, and if we add 100 particles in 1 frame it will cause jerks, also if we do it asynchronously in n milliseconds it may still not work stable until we slow it down enough to stop causing jerks, and slow lengthening and shortening is not really what is needed. Another thing is to collect a rope from particles of another rope while its modeling is turned off, and after turning it on, set velocity on each particle to 0 (I hope so we can prevent jerks) and then we can stably replace one rope with another, with longer ends on those parts where the first rope had no particles at all. The question is this:
- how to get the positions and velocities of a separate ObiRope
- how to safely place all new particles on the places of old ones before starting modeling of the copied rope, while adding missing ones to the beginning or end.
- How to turn on the copied rope and avoid jerks and vibrations.

As you can see from the code, I create a BluePrint from the code according to the distance from object a to object b, and sometimes there is an object in the middle between a and b. And as far as I know the number of particles is created automatically. So in one case where there are 10 unit meters from a to b, roughly speaking a rope of 10 particles will be created. The rope to be created at a different distance, for example we add object c and we need to extend it to 15 units. So we will have two ropes, one with 15 particles, the other with 10. If I copy the positions from 1 to 10 particles, the 5 will remain in the old places and there will be a jump. How to avoid this.


or another question, how to create a rope with 10 units, and already copy the positions of the first to the second. In the code I provided I don't control the number of particles, and they are created depending on the distance of the two ControlPoints.
Maybe to create new ControlPoints I can copy the positions from the old ControlPoints from the first rope. Then FlushEvents on the new rope should logically create the same number of particles than the current number of particles on the old rope.
Reply
#4
Yes, solver.positions contains particle data for all ropes in the solver. However each rope has an “elements” array, each entry in this array contains two indices into solver.positions that you can use to retrieve the correct position data for particles in a specific rope, in the same order they appear in the rope. See the link in my previous message:
http://obi.virtualmethodstudio.com/manua...ropes.html

The easiest way to add new particles is to use a cursor. Cursors will add as many particles as needed to reach a specific length in a single frame, however they won’t set the position of said particles - hence the “jerk” as the simulation takes over and moves the newly added particles in the following frames. However you will be immediately copying particle positions over from another rope as soon as the cursor creates them, so there should not be any jerks/sudden motion. (See the GrapplingHook sample scene for an example of using a cursor in a similar way)

Tomorrow morning I will whip up an example of this to help you out.

Kind regards,
Reply
#5
(16-01-2024, 07:42 PM)josemendez Wrote: Yes, solver.positions contains particle data for all ropes in the solver. However each rope has an “elements” array, each entry in this array contains two indices into solver.positions that you can use to retrieve the correct position data for particles in a specific rope, in the same order they appear in the rope. See the link in my previous message:
http://obi.virtualmethodstudio.com/manua...ropes.html

The easiest way to add new particles is to use a cursor. Cursors will add as many particles as needed to reach a specific length in a single frame, however they won’t set the position of said particles - hence the “jerk” as the simulation takes over and moves the newly added particles in the following frames. However you will be immediately copying particle positions over from another rope as soon as the cursor creates them, so there should not be any jerks/sudden motion. (See the GrapplingHook sample scene for an example of using a cursor in a similar way)

Tomorrow morning I will whip up an example of this to help you out.

Kind regards,
Thank you, yeap i'am use yours code from grapplingHook
Reply
#6
Here's a basic script that ensures both ropes have the same amount of particles and elements, then copies particle positions/velocities from one rope to the other:

Code:
using UnityEngine;
using Obi;

public class CopyRope : MonoBehaviour
{
    public ObiRope src;
    public ObiRope dst;

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.C))
            Copy();
    }

    void Copy()
    {
        // store inital amount of elements in destination rope:
        int initialElementCount = dst.elements.Count;

        // if the destination has more particles than the source, remove excess particles/elements.
        while (dst.activeParticleCount > src.activeParticleCount)
        {
            dst.elements.RemoveAt(dst.elements.Count - 1);
            dst.DeactivateParticle(dst.activeParticleCount - 1);
        }

        // if the destination has less particles than the source, add more particles/elements.
        while (dst.activeParticleCount < src.activeParticleCount)
        {
            // create a new element joining the last two particles, of length equal
            // to the average distance between particles:
            dst.elements.Add(new ObiStructuralElement
            {
                particle1 = dst.elements[dst.elements.Count - 1].particle2,
                particle2 = dst.solverIndices[dst.activeParticleCount],
                restLength = dst.interParticleDistance
            });

            // copy data from the last active particle to the first inactive one, then activate it.
            dst.CopyParticle(dst.activeParticleCount-1, dst.activeParticleCount);
            dst.ActivateParticle(dst.activeParticleCount);
        }

        // now that both ropes have same amount of particles/elements,
        // just copy positions/velocities:
        int srcParticle, dstParticle;
        for (int i = 0; i < src.elements.Count; ++i)
        {
            srcParticle = src.elements[i].particle1;
            dstParticle = dst.elements[i].particle1;
            dst.solver.positions[dstParticle] = src.solver.positions[srcParticle];
            dst.solver.velocities[dstParticle] = src.solver.velocities[srcParticle];
        }

        // don't forget to copy last particle too:
        srcParticle = src.elements[src.elements.Count - 1].particle2;
        dstParticle = dst.elements[dst.elements.Count - 1].particle2;
        dst.solver.positions[dstParticle] = src.solver.positions[srcParticle];
        dst.solver.velocities[dstParticle] = src.solver.velocities[srcParticle];

        // if the amount of elements has changed, rebuild constraints:
        if (initialElementCount != dst.elements.Count)
            dst.RebuildConstraintsFromElements();
    }
}

You could also use a cursor and replace the first two while loops with calls to ChangeLength(), however since cursors work in terms of length and we are interested in directly counting particles, it's simpler to handle elements and particles by hand.

Also note this script assumes blueprint resolution is the same for both ropes (so that the gap between particles and the size of the particles is the same for both), and that they have non-zero pool sizes (so that there's inactive particles available to be used).

let me know if you need further help!
Reply