Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Trying to add DistanceConstraint for my TearableCloth but getting IndexOutOfRange
#1
Pregunta 
I'm following the example on the official document, here is the jist of my current method:

Code:
// get a hold of the distance constraint
        var distanceConstraints = colon.GetConstraintsByType(Oni.ConstraintType.Distance) as ObiConstraints<ObiDistanceConstraintsBatch>;

        // create a new distance constraints batch
        var batch = new ObiDistanceConstraintsBatch();

        // Add a constraint for the pair
        batch.AddConstraint(new Vector2Int(particleSolverIndexA, particleSolverIndexB), 0);

        // set the amount of active constraints in the batch to 2 (the ones we just added).
        batch.ActivateConstraint(0);

        // append the batch to the pin constraints:
        distanceConstraints.AddBatch(batch);

        // this will cause the solver to rebuild distance constraints at the beginning of the next frame:
        colon.SetConstraintsDirty(Oni.ConstraintType.Distance);

Basically my method will first find the solver indexes of two particles closest to the given world positions, then using the above code to add DistanceConstraint between them.

I removed the part of the example where it clears the entire constraint batches first, because I don't wanna remove all the existing DistanceConstraint.

Is there something wrong in the above code? Or I'm not getting the correct solver index for my particles?
Reply
#2
Hi!

When you add an actor to a solver, all its particle and constraint data are copied into the solver. Each particle in the actor -numbered from 0 to the amount of particles in the actor- is assigned a position in the solver's arrays, its position in the solver is its solver index. The range of a solver index is from 0 to the combined amount of all particles being simulated by the solver.

In your code, "colon" is probably an actor (cloth or softbody), not a solver. Is this correct?

If that's the case, you're retrieving the actor's constraints (whose particle indices run from 0 to the amount of particles in the actor):

Code:
var distanceConstraints = colon.GetConstraintsByType(Oni.ConstraintType.Distance) as ObiConstraints<ObiDistanceConstraintsBatch>;

But then you're passing indices of particles in the entire solver (which run from 0 to the total amount of particles in the solver):

Code:
// Add a constraint for the pair
batch.AddConstraint(new Vector2Int(particleSolverIndexA, particleSolverIndexB), 0);

which will result in an out of index access since particleSolverIndexA and B are probably much larger than the amount of particles in that actor.


Either use actor indices, or access the solver's batches (instead of the actor batches).

kind regards,
Reply
#3
(27-07-2022, 07:20 AM)josemendez Wrote: Hi!

When you add an actor to a solver, all its particle and constraint data are copied into the solver. Each particle in the actor -numbered from 0 to the amount of particles in the actor- is assigned a position in the solver's arrays, its position in the solver is its solver index. The range of a solver index is from 0 to the combined amount of all particles being simulated by the solver.

In your code, "colon" is probably an actor (cloth or softbody), not a solver. Is this correct?

If that's the case, you're retrieving the actor's constraints (whose particle indices run from 0 to the amount of particles in the actor):

Code:
var distanceConstraints = colon.GetConstraintsByType(Oni.ConstraintType.Distance) as ObiConstraints<ObiDistanceConstraintsBatch>;

But then you're passing indices of particles in the entire solver (which run from 0 to the total amount of particles in the solver):

Code:
// Add a constraint for the pair
batch.AddConstraint(new Vector2Int(particleSolverIndexA, particleSolverIndexB), 0);

which will result in an out of index access since particleSolverIndexA and B are probably much larger than the amount of particles in that actor.


Either use actor indices, or access the solver's batches (instead of the actor batches).

kind regards,

Ohhhhh, that was very dumb of me.

I think it get it to work now, no errors anymore, however I can't tell the difference after DistanceConstraints are added. AddConstraint() for DistanceConstraint only takes two parameters, are there more for me to tweak after adding one? Like strength or something?
Reply
#4
(27-07-2022, 09:54 PM)snowtv Wrote: Ohhhhh, that was very dumb of me.

I think it get it to work now, no errors anymore, however I can't tell the difference after DistanceConstraints are added. AddConstraint() for DistanceConstraint only takes two parameters, are there more for me to tweak after adding one? Like strength or something?

There can be several reasons for this. Could you share the entire code you're using?

For instance, a possible cause would be adding the distance constraint to the blueprint, once the blueprint has been instantiated. This will have no effect on existing instances of it.
Reply
#5
(28-07-2022, 07:36 AM)josemendez Wrote: There can be several reasons for this. Could you share the entire code you're using?

For instance, a possible cause would be adding the distance constraint to the blueprint, once the blueprint has been instantiated. This will have no effect on existing instances of it.

Code:
public void AttachParticlePairInColon(ObiTearableCloth colon, int colonMeshVertexA, int colonMeshVertexB)
    {
        ClothBlueprintParticleIndividualizer attacher = colon.GetComponent<ClothBlueprintParticleIndividualizer>();
        Mesh colonMesh = colon.GetComponent<MeshFilter>().sharedMesh;

        int particleSolverIndexA = attacher.GetClosestParticleSolverIndex(colon.transform.TransformPoint(colonMesh.vertices[colonMeshVertexA]));
        int particleSolverIndexB = attacher.GetClosestParticleSolverIndex(colon.transform.TransformPoint(colonMesh.vertices[colonMeshVertexB]));

        // get a hold of the distance constraint
        var distanceConstraints = obiSolver.GetConstraintsByType(Oni.ConstraintType.Distance) as ObiConstraints<ObiDistanceConstraintsBatch>;

        // create a new distance constraints batch
        var batch = new ObiDistanceConstraintsBatch();

        // Add a constraint for the pair
        batch.AddConstraint(new Vector2Int(particleSolverIndexA, particleSolverIndexB), 0);

        // set the amount of active constraints in the batch to 2 (the ones we just added).
        batch.ActivateConstraint(0);

        // append the batch to the pin constraints:
        distanceConstraints.AddBatch(batch);

        // this will cause the solver to rebuild distance constraints at the beginning of the next frame:
        colon.SetConstraintsDirty(Oni.ConstraintType.Distance);
    }

This is it. I think I'm adding them to the solver? Does it have anything to do with the fact that I'm using TearableCloth? I'm also thinking about creating invisible objects and use PinConstraints if DistanceConstraint is not an option...
Reply
#6
(28-07-2022, 07:19 PM)snowtv Wrote: I think I'm adding them to the solver?

Yes you're adding them to the solver, but at the very end you're asking the solver to rebuild all distance constraint batches, by calling SetConstraintsDirty(). From the docs:

Quote:After you've added or removed constraints, you will need to call actor.SetConstraintsDirty(). This rebuilds all solver batches of the given constraint type.


So what's happening is that you're adding a new constraint to the solver batches, and then asking the solver to discard all existing distance batches and rebuild them by merging batch data from all actors in the solver.

Either add the constraint to the actor blueprint (useful if you want to later instantiate the actor multiple times), or add the constraint to the solver if it's a one-time thing. In this case, don't call SetConstraintsDirty()!.
Reply
#7
(29-07-2022, 07:59 AM)josemendez Wrote: Yes you're adding them to the solver, but at the very end you're asking the solver to rebuild all distance constraint batches, by calling SetConstraintsDirty(). From the docs:



So what's happening is that you're adding a new constraint to the solver batches, and then asking the solver to discard all existing distance batches and rebuild them by merging batch data from all actors in the solver.

Either add the constraint to the actor blueprint (useful if you want to later instantiate the actor multiple times), or add the constraint to the solver if it's a one-time thing. In this case, don't call SetConstraintsDirty()!.


I thought it's necessary to SetConstraintsDirty() because when adding new constraint to a batch it's possible that the new constraint contains particles that are already in that batch?

I know it's not the case for my issue because I'm adding a constraint to a new batch and adding the new batch to the solver, so I'm guessing it's ok to have as many batches as you need?

Also why rebuilding batches will render the constraints useless? I thought it only changed how the constraint data is arranged in the batches, but the actual data which affects the physics behavior won't be different?



Just to update, I commented out the line that calls SetConstraintsDirty(), still don't see a difference...
Reply
#8
(29-07-2022, 07:27 PM)snowtv Wrote: I thought it's necessary to SetConstraintsDirty() because when adding new constraint to a batch it's possible that the new constraint contains particles that are already in that batch?

Not really, SetConstraintsDirty() is somewhat related to batches but it does not perform constraint coloring.

Constraints are colored -that is, grouped into batches according to shared particles- when you generate the blueprint. This is a very costly step as it has quadratic cost (O(n^2) over the amount of constraints of each type). This is usually never done at runtime. If you do add constraints at runtime, it's usually a small amount of them and quite simple to keep them in separate batches when adding them. If you need to add a large amount of constraints and it's not trivial to determine how to colorize them (separate them into batches), you can use GraphColoring.Colorize() to get a batch index for each one.

So when you Generate() a blueprint, particles are created, constraints are created, and they're grouped into batches using GraphColoring.Colorize().

http://obi.virtualmethodstudio.com/manua...aints.html

Quote:Arranging constraints into batches can be a very time consuming process (known as graph-coloring), so it is generally not done at runtime: it happens as part of blueprint generation, so blueprints store constraints pre-grouped into batches.


The whole point of batching constraints is to be able to apply constraints using multithreading, without threads stepping on each other's toes. However, the more batches you have, the less parallelism you can squeeze out since batches must be done sequentially. Ideally you want as few batches as possible. So when you add an actor to a solver, the solver cannot just append the batches in that actor's blueprint to all the batches already in the solver: it needs to somehow merge all them, minimizing the total batch amount to maximize speed.

This is what SetConstraintsDirty() asks the solver to do: clear all constraint batches in the solver, take all batches from all actor's blueprints, and create new solver batches by merging them in a way that the total amount of batches is minimized. Since actors do not share particles, this is trivial to do: just take the first batch from each actor and merge them all. Then take the second batch from each actor and merge them all, etc. So you end up with only as many batches as the largest actor in the solver, which is way better than the sum of all batches from all actors.

http://obi.virtualmethodstudio.com/manua...aints.html

Quote:Very much like particle data, constraint data isn't laid out in the solver the same way it is in an actor. When an actor gets added to a solver, all its constraints are merged with the existing constraints in the solver to maximize performance.


(29-07-2022, 07:27 PM)snowtv Wrote: so I'm guessing it's ok to have as many batches as you need?

It's ok as in "it will work". Ideally, you want as few batches as possible if you want multithreading to be of any benefit. For instance: if you had one batch per constraint, then all constraints would be processed in a single thread. This works, but it's slow.


(29-07-2022, 07:27 PM)snowtv Wrote:
Also why rebuilding batches will render the constraints useless? I thought it only changed how the constraint data is arranged in the batches, but the actual data which affects the physics behavior won't be different?

Because it ditches all constraints in the solver, and rebuilds them using the constraints found in the actors blueprints.

(29-07-2022, 07:27 PM)snowtv Wrote: Just to update, I commented out the line that calls SetConstraintsDirty(), still don't see a difference...

That's unexpected. Would it be possible for you to share a small scene that reproduces this issue, so that I can better understand the context in which you're doing this? (send it to support(at)virtualmethodstudio.com) thanks!
Reply
#9
(01-08-2022, 08:11 AM)josemendez Wrote:
Not really, SetConstraintsDirty() is somewhat related to batches but it does not perform constraint coloring.

Constraints are colored -that is, grouped into batches according to shared particles- when you generate the blueprint. This is a very costly step as it has quadratic cost (O(n^2) over the amount of constraints of each type). This is usually never done at runtime. If you do add constraints at runtime, it's usually a small amount of them and quite simple to keep them in separate batches when adding them. If you need to add a large amount of constraints and it's not trivial to determine how to colorize them (separate them into batches), you can use GraphColoring.Colorize() to get a batch index for each one.

So when you Generate() a blueprint, particles are created, constraints are created, and they're grouped into batches using GraphColoring.Colorize().

http://obi.virtualmethodstudio.com/manua...aints.html



The whole point of batching constraints is to be able to apply constraints using multithreading, without threads stepping on each other's toes. However, the more batches you have, the less parallelism you can squeeze out since batches must be done sequentially. Ideally you want as few batches as possible. So when you add an actor to a solver, the solver cannot just append the batches in that actor's blueprint to all the batches already in the solver: it needs to somehow merge all them, minimizing the total batch amount to maximize speed.

This is what SetConstraintsDirty() asks the solver to do: clear all constraint batches in the solver, take all batches from all actor's blueprints, and create new solver batches by merging them in a way that the total amount of batches is minimized. Since actors do not share particles, this is trivial to do: just take the first batch from each actor and merge them all. Then take the second batch from each actor and merge them all, etc. So you end up with only as many batches as the largest actor in the solver, which is way better than the sum of all batches from all actors.

http://obi.virtualmethodstudio.com/manua...aints.html




It's ok as in "it will work". Ideally, you want as few batches as possible if you want multithreading to be of any benefit. For instance: if you had one batch per constraint, then all constraints would be processed in a single thread. This works, but it's slow.



Because it ditches all constraints in the solver, and rebuilds them using the constraints found in the actors blueprints.


That's unexpected. Would it be possible for you to share a small scene that reproduces this issue, so that I can better understand the context in which you're doing this? (send it to support(at)virtualmethodstudio.com) thanks!

Hi, thank you very much for such detailed explanations. I'm currently trying to make a small Unity project that hopefully can recreate the problem but I'm getting some errors right now. I will email the sample project asap!
Reply
#10
(01-08-2022, 08:11 AM)josemendez Wrote:
Not really, SetConstraintsDirty() is somewhat related to batches but it does not perform constraint coloring.

Constraints are colored -that is, grouped into batches according to shared particles- when you generate the blueprint. This is a very costly step as it has quadratic cost (O(n^2) over the amount of constraints of each type). This is usually never done at runtime. If you do add constraints at runtime, it's usually a small amount of them and quite simple to keep them in separate batches when adding them. If you need to add a large amount of constraints and it's not trivial to determine how to colorize them (separate them into batches), you can use GraphColoring.Colorize() to get a batch index for each one.

So when you Generate() a blueprint, particles are created, constraints are created, and they're grouped into batches using GraphColoring.Colorize().

http://obi.virtualmethodstudio.com/manua...aints.html



The whole point of batching constraints is to be able to apply constraints using multithreading, without threads stepping on each other's toes. However, the more batches you have, the less parallelism you can squeeze out since batches must be done sequentially. Ideally you want as few batches as possible. So when you add an actor to a solver, the solver cannot just append the batches in that actor's blueprint to all the batches already in the solver: it needs to somehow merge all them, minimizing the total batch amount to maximize speed.

This is what SetConstraintsDirty() asks the solver to do: clear all constraint batches in the solver, take all batches from all actor's blueprints, and create new solver batches by merging them in a way that the total amount of batches is minimized. Since actors do not share particles, this is trivial to do: just take the first batch from each actor and merge them all. Then take the second batch from each actor and merge them all, etc. So you end up with only as many batches as the largest actor in the solver, which is way better than the sum of all batches from all actors.

http://obi.virtualmethodstudio.com/manua...aints.html




It's ok as in "it will work". Ideally, you want as few batches as possible if you want multithreading to be of any benefit. For instance: if you had one batch per constraint, then all constraints would be processed in a single thread. This works, but it's slow.



Because it ditches all constraints in the solver, and rebuilds them using the constraints found in the actors blueprints.


That's unexpected. Would it be possible for you to share a small scene that reproduces this issue, so that I can better understand the context in which you're doing this? (send it to support(at)virtualmethodstudio.com) thanks!

Hello, I sent an email with the sample project yesterday, did you receive it?
Reply