Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Bug / Crash  Rod loses initial shape when forced
#1
There is rod with some bended initial shape and I push it into place where it's not possible to keep this shape fully. In my case it's some kind of tunnel collider.
The problem is that rod loses it's shape during that operation, like it had plastic deformation enabled. Meanwhile plastic creep and yield is set to 0.
Is that expected outcome?

Example of such a deformation below.
All constraint values are set to default (0).


Attached Files Thumbnail(s)
       
Reply
#2
(12-08-2025, 03:58 PM)Qriva0 Wrote: There is rod with some bended initial shape and I push it into place where it's not possible to keep this shape fully. In my case it's some kind of tunnel collider.
The problem is that rod loses it's shape during that operation, like it had plastic deformation enabled. Meanwhile plastic creep and yield is set to 0.
Is that expected outcome?

Example of such a deformation below.
All constraint values are set to default (0).
The doc says: https://obi.virtualmethodstudio.com/manu...aints.html

Plastic yield

Strain threshold that marks the transition from elastic to plastic deformation. Low values will make it easier to permanently deform the constraint. At high values it will take more violent deformation to permanently deform the constraint.

So set it to 0 is wrong in your case. But it also says this so I assume there is a bug as percentage 0 should mean no deformation.

Plastic creep
Once the plastic yield threshold is surpassed, plastic creep determines what percentage of the deformation is permanently absorbed by the constraint.
Reply
#3
(13-08-2025, 03:35 AM)chenji Wrote: So set it to 0 is wrong in your case. But it also says this so I assume there is a bug as percentage 0 should mean no deformation.

Plastic creep
Once the plastic yield threshold is surpassed, plastic creep determines what percentage of the deformation is permanently absorbed by the constraint.

Yes that is what I mean, if it's 0 then there should be no problem, but the same happens if it's greater than 0.

However I think there might be something else wrong, because I tried to display restDarbouxVectors for each particle and (assuming I did it correctly) I doubt there should be NaN's there.
Is that bug and perhaps related to my problem?


Attached Files Thumbnail(s)
   
Reply
#4
(13-08-2025, 04:22 PM)Qriva0 Wrote: There is rod with some bended initial shape and I push it into place where it's not possible to keep this shape fully. In my case it's some kind of tunnel collider.
The problem is that rod loses it's shape during that operation, like it had plastic deformation enabled. Meanwhile plastic creep and yield is set to 0.
Is that expected outcome?

Hi!

Plastic creep is the amount of deformation that is allowed to creep into the object's rest shape, once deformed past the plastic yield threshold. Setting it to zero disables plasticity, so no amount of deformation should be absorbed by the rest state of the rod.

Tested this by jamming a straight rod into a curved collider and then disabling the collider, but the rod immediately recovered its initial shape. Here's a video of the behavior you should expect using plastic creep = 1 (the rod retains the shape of the collider it was jammed into, even after the collider is gone) and plastic creep = 0 (the rod goes back to straight after the collider disappears).



(13-08-2025, 04:22 PM)Qriva0 Wrote: However I think there might be something else wrong, because I tried to display restDarbouxVectors for each particle and (assuming I did it correctly) I doubt there should be NaN's there.
Is that bug and perhaps related to my problem?

Particles don't have darboux vectors (either rest or current), constraints do. They're accessed very differently. How are you looking at these values? Data lists in Obi usually are uninitialized, so unused areas of them may have any value.
Reply
#5
(14-08-2025, 11:45 AM)josemendez Wrote: Plastic creep is the amount of deformation that is allowed to creep into the object's rest shape, once deformed past the plastic yield threshold. Setting it to zero disables plasticity, so no amount of deformation should be absorbed by the rest state of the rod.

Exactly, however I thought that maybe some precision error builds up or something, so I commented code in BendTwistBatch, to be precise those lines:
Code:
// plasticity
if (math.lengthsq(omega.value.xyz) > plasticity[i].x * plasticity[i].x)
{
    rest.value += omega.value * plasticity[i].y * deltaTime;
    restDarboux[i] = rest;
}

but it still breaks and problem persists.

(14-08-2025, 11:45 AM)josemendez Wrote: They're accessed very differently. How are you looking at these values? Data lists in Obi usually are uninitialized, so unused areas of them may have any value.

I did something like this:
Code:
private void OnDrawGizmos()
{
    ObiRod rod = GetComponent<ObiRod>();
    if (rod == null || !rod.isLoaded)
    {
        return;
    }

    var solverConstraints = rod.solver.GetConstraintsByType(Oni.ConstraintType.BendTwist) as ObiConstraints<ObiBendTwistConstraintsBatch>;
    if (solverConstraints != null)
    {
        for (int j = 0; j < solverConstraints.batchCount; ++j)
        {
            var batch = solverConstraints.GetBatch(j) as ObiBendTwistConstraintsBatch;

            for (int i = 0; i < batch.activeConstraintCount; i++)
            {
                int indexA = batch.particleIndices[i * 2];
                int indexB = batch.particleIndices[i * 2 + 1];

                //Gizmos.DrawWireSphere(rod.solver.positions[indexA], 0.5f);
                UnityEditor.Handles.Label(rod.solver.positions[indexA], batch.restDarbouxVectors[i * 2].ToString());
                //Gizmos.color = Color.yellow;
                //Gizmos.DrawRay(rod.solver.positions[indexA], (rod.solver.orientations[indexA] * batch.restDarbouxVectors[i * 2]) * Vector3.forward);
            }
        }
    }
}

In my case currently there is mesh collider with distance field as tunnel (but the same happens for simple boxes) and yes, your video shows what should happen, but take into account that your rod is not bended from the start and this might affect results. I try to push into stright tunnel already bended rod and while inside of it it makes sense that it bends, but after pulling out it should go back to initial shape.
Here I have video of pushing the rod into hole made of boxes, I failed to push it inside, but notice the same thing happened and after leaving it, the rod tip is not perfectly roundly bended.
I attached constraint values of my rod and it looks like I can't attach video clips, so I fast uploaded it here (but will expire in 2 days): https://streamable.com/cyt3k5


Attached Files Thumbnail(s)
   
Reply
#6
(14-08-2025, 01:02 PM)Qriva0 Wrote: Exactly, however I thought that maybe some precision error builds up or something, so I commented code in BendTwistBatch, to be precise those lines:
Code:
// plasticity
if (math.lengthsq(omega.value.xyz) > plasticity[i].x * plasticity[i].x)
{
    rest.value += omega.value * plasticity[i].y * deltaTime;
    restDarboux[i] = rest;
}

but it still breaks and problem persists.

I've been trying to reproduce this to no avail. Tested with a rod that's curved at rest, sliding it into a straight collider, then disabling the collider: the rod quickly recovers its initial shape. Would it be possible for you to send a repro scene, so that I can take a look at what's going on?

(14-08-2025, 01:02 PM)Qriva0 Wrote: I did something like this:
Code:
private void OnDrawGizmos()
{
    ObiRod rod = GetComponent<ObiRod>();
    if (rod == null || !rod.isLoaded)
    {
        return;
    }

    var solverConstraints = rod.solver.GetConstraintsByType(Oni.ConstraintType.BendTwist) as ObiConstraints<ObiBendTwistConstraintsBatch>;
    if (solverConstraints != null)
    {
        for (int j = 0; j < solverConstraints.batchCount; ++j)
        {
            var batch = solverConstraints.GetBatch(j) as ObiBendTwistConstraintsBatch;

            for (int i = 0; i < batch.activeConstraintCount; i++)
            {
                int indexA = batch.particleIndices[i * 2];
                int indexB = batch.particleIndices[i * 2 + 1];

                //Gizmos.DrawWireSphere(rod.solver.positions[indexA], 0.5f);
                UnityEditor.Handles.Label(rod.solver.positions[indexA], batch.restDarbouxVectors[i * 2].ToString());
                //Gizmos.color = Color.yellow;
                //Gizmos.DrawRay(rod.solver.positions[indexA], (rod.solver.orientations[indexA] * batch.restDarbouxVectors[i * 2]) * Vector3.forward);
            }
        }
    }
}

There's only one darboux vector per constraint (check API docs for ObiBendTwistConstraintsBatch), however your code assumes there's two for some reason (multiplying i by 2 when accessing batch.restDarbouxVectors). As a result you're skipping every other entry in the array, and end up reading data that's out of bounds - that's where the NaNs come from. Just remove the "*2" when accessing the restDarbouxVectors array.

kind regards,
Reply
#7
(15-08-2025, 12:10 PM)josemendez Wrote: I've been trying to reproduce this to no avail. Tested with a rod that's curved at rest, sliding it into a straight collider, then disabling the collider: the rod quickly recovers its initial shape. Would it be possible for you to send a repro scene, so that I can take a look at what's going on?

Was finally able to reproduce by using the modified BurstBendTwistConstraintsBatch.cs file I shared in your other thread regarding pin constraints, I was using the version downloaded from the store. Make sure the disambiguation code (right before the plasticity code you tried commenting out) looks as in the original, retail version of the file:

Code:
quaternion omega_plus;
omega_plus.value = omega.value + rest.value;  //delta Omega with - omega_0
omega.value -= rest.value;                               //delta Omega with + omega_0
if (math.lengthsq(omega.value) > math.lengthsq(omega_plus.value))
    omega = omega_plus;

Checking only the real part of the quaternion is not correct for bend/twist constraints.

Let me know how it goes,

kind regards
Reply
#8
Quote:There's only one darboux vector per constraint (check API docs for ObiBendTwistConstraintsBatch), however your code assumes there's two for some reason (multiplying i by 2 when accessing batch.restDarbouxVectors). As a result you're skipping every other entry in the array, and end up reading data that's out of bounds - that's where the NaNs come from. Just remove the "*2" when accessing the restDarbouxVectors array.

Aww you are right, I copied the code without a second thought, it makes sense.

(15-08-2025, 12:34 PM)josemendez Wrote: Was finally able to reproduce by using the modified BurstBendTwistConstraintsBatch.cs file I shared in your other thread regarding pin constraints, I was using the version downloaded from the store. Make sure the disambiguation code (right before the plasticity code you tried commenting out) looks as in the original, retail version of the file:

Code:
quaternion omega_plus;
omega_plus.value = omega.value + rest.value;  //delta Omega with - omega_0
omega.value -= rest.value;                               //delta Omega with + omega_0
if (math.lengthsq(omega.value) > math.lengthsq(omega_plus.value))
    omega = omega_plus;

Checking only the real part of the quaternion is not correct for bend/twist constraints.

Let me know how it goes,

You save the day again!
Yes, it seems to work again. Can you update that file in the other thread just in case, so nobody else get the same problem?
Reply