Obi Official Forum

Full Version: RemoveConstraint error
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
version: 6.5

I tested the runtime delete Constraint,The m_IDs of solverBatch is empty. Why?
Error message:


ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index

System.Collections.Generic.List`1[T].RemoveAt (System.Int32 index) (at <6073cf49ed704e958b8a66d540dea948>:0)
Obi.ObiConstraintsBatch.RemoveConstraint (System.Int32 constraintIndex) (at Assets/Obi/Scripts/Common/Blueprints/Constraints/Batches/ObiConstraintsBatch.cs:183)




Code:
        var dc = m_Actor.GetConstraintsByType(Oni.ConstraintType.ShapeMatching) as ObiConstraints<ObiShapeMatchingConstraintsBatch>;
        var sc = m_Actor.solver.GetConstraintsByType(Oni.ConstraintType.ShapeMatching) as ObiConstraints<ObiShapeMatchingConstraintsBatch>;

        for (int j = 0; j < dc.GetBatchCount(); j++)
        {
            var batch = dc.batches[j];
            var solverBatch = sc.batches[j];
            int offset = m_Actor.solverBatchOffsets[(int)Oni.ConstraintType.ShapeMatching][j];
            for (int i = 0; i < batch.activeConstraintCount; ++i)
            {
                for (int k = 0; k < batch.numIndices[i]; ++k)
                {
                    int p = batch.particleIndices[batch.firstIndex[i] + k];
                    int or = m_Actor.solverIndices[p];
                    Vector3 shapePos = m_Actor.solver.positions[or];
                    if (box.bounds.Contains(shapePos))
                    {
                       
                        solverBatch.RemoveConstraint(i);
                        //batch.RemoveConstraint(i);
                    }
                }

            }
        }
        m_Actor.SetConstraintsDirty(Oni.ConstraintType.ShapeMatching);
But no error was reported when I tried to delete the batch, and the deletion was successful.
Hi!

There's a coupe problems with this code. First, you're mixing up actor batches and solver batches. You are iterating trough actor batches, but removing constraints from the solver batch. Not sure this is what you want to do. Since the amount of constraints in the actor doesn't change at all as you remove constraints from the solver, you will end up trying to remove a constraint that doesn't exist. Your code is equivalent to this:

Quote:for i = 0 to 5,
remove constraint (i);

You can clearly see the problem here: when you remove constraint 0, there's only 4 constraints left. Then you remove constraint 1, and there's 3 left. Then you remove constraint 2, there's 2 left. Then you try to remove constraint 3 (and later, 4 and 5) but there's only 2 left, so ArgumentOutOfRangeException.

The manual explains the difference between both types of batches and how to deal with them. See "Per-constraint parameters" in this page:
http://obi.virtualmethodstudio.com/manua...aints.html

Second issue is that you're removing constraints as you're iterating trough them. This will skip one constraint every time you remove any. As a rule of thumb in programming: never, ever remove stuff from a list or array while you're iterating trough it from start to end. Either store the indices of the stuff you need removed and then remove them later, or iterate backwards. See: https://www.techiedelight.com/remove-ele...ng-csharp/

kind regards,
1. When I remove actor batches of constraints, they'll update the local Blueprint source, which isn't what I wanted.
If I want to remove the constraint specified at run time, should I remove it from the actor batch?
2. This is my silly mistake, thank you for pointing it out
(10-01-2023, 11:53 AM)Seahorse Wrote: [ -> ]1. When I remove actor batches of constraints, they'll update the local Blueprint source, which isn't what I wanted.
If I want to remove the constraint specified at run time, should I remove it from the actor batch?

When an actor gets added to a solver at runtime, its local constraint batches (or rather, the ones in its blueprint) are copied and inserted into the solver batches, together with the constraints of all other actors that have been previously added to the solver.

So you remove it from the solver's batch. However you don't remove the one at index i, you remove the one at index offset + i. The offset is the index at which the first constraint pertaining to that actor is located inside the solver.
Code:
        public void RemoveConstraint(int constraintIndex)
        {
            SwapConstraints(constraintIndex, constraintCount - 1);
            m_IDs.RemoveAt(constraintCount - 1);
            m_IDToIndex.RemoveAt(constraintCount - 1);

            m_ConstraintCount--;
            m_ActiveConstraintCount = Mathf.Min(m_ActiveConstraintCount, m_ConstraintCount);
        }
One mystery: Why is the count of m_IDs and m_IDToIndex is 0? solver batch
(10-01-2023, 12:48 PM)Seahorse Wrote: [ -> ]
Code:
        public void RemoveConstraint(int constraintIndex)
        {
            SwapConstraints(constraintIndex, constraintCount - 1);
            m_IDs.RemoveAt(constraintCount - 1);
            m_IDToIndex.RemoveAt(constraintCount - 1);

            m_ConstraintCount--;
            m_ActiveConstraintCount = Mathf.Min(m_ActiveConstraintCount, m_ConstraintCount);
        }
One mystery: Why is the count of m_IDs and m_IDToIndex is 0? solver batch

Because these are only populated for actor/blueprint batches.

I think I misunderstood what you were trying to do here. Typically, you can only destroy constraints from the solver when they're not part of any specific actor, for instance pin or stitch constraints. If you're dealing with shape matching constraints that haven't been manually added to the solver (but added as part of an existing actor), you have two options:

A) - Take the entire actor out of the solver, destroy the constraints in the actor's blueprint (or add new constraints, reorder them, anything goes), then put the actor back into the solver. This is the most flexible approach, but also the less performant.

When using this method, typically you want to create an instance of the blueprint at runtime to avoid modifying the one stored as an asset in your project.

So, something like this:

Code:
// create an instance of the original blueprint:
softbody.softbodyBlueprint = Instantiate(softbody.softbodyBlueprint);

// take the actor out of the solver:
softbody.RemoveFromSolver();

// do anything you want with the blueprint, add, remove, deactivate constraints, etc. Here I'm just regenerating it from scratch.
softbody.softbodyBlueprint.GenerateImmediate();

//place the actor back into the solver:
softbody.AddToSolver();

B) - Instead of completely removing the constraints, deactivate them. Then, just flag them dirty so that the solver batches are updated. This is closer to what you were originally doing. For instance:

Code:
var dc = m_Actor.GetConstraintsByType(Oni.ConstraintType.ShapeMatching) as ObiConstraints<ObiShapeMatchingConstraintsBatch>;

        for (int j = 0; j < dc.GetBatchCount(); j++)
        {
            var batch = dc.batches[j];
            for (int i = batch.activeConstraintCount-1; i >= 0; --i)
            {
                for (int k = 0; k < batch.numIndices[i]; ++k)
                {
                    int p = batch.particleIndices[batch.firstIndex[i] + k];
                    int or = m_Actor.solverIndices[p];
                    Vector3 shapePos = m_Actor.solver.positions[or];
                    if (box.bounds.Contains(shapePos))
                    {
                        batch.DeactivateConstraint(i);
                    }
                }

            }
        }
        m_Actor.SetConstraintsDirty(Oni.ConstraintType.ShapeMatching);

let me know if you need further help,

cheers!
Thank you very much. That's what I wanted.