Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Particle Collision API indexing wrong?
#1
Using the example I added gizoms to debug some weird behavior I was seeing;
[Image: DIES4.png?trs=2f6602c6f954098069b38316a7...d4c41f5ae6]
Code;
Code:
public class ZeroGravityZone : MonoBehaviour
{
    [SerializeField]
    private ObiSolver solver;
    public float antiGravityScale = 2;

    ObiSolver.ObiCollisionEventArgs collisionEvent;

    [AutoProperty, SerializeField, ReadOnly]
    private Collider antiGravityCollider;

    void OnEnable()
    {
        solver.OnCollision += Solver_OnCollision;
    }

    void OnDisable()
    {
        solver.OnCollision -= Solver_OnCollision;
    }

    private List<Vector3> collisionPositions = new List<Vector3>();

    void Solver_OnCollision(object sender, Obi.ObiSolver.ObiCollisionEventArgs e)
    {
        // calculate an acceleration that counteracts gravity:
        Vector4 antiGravityAccel = -(Vector4)solver.parameters.gravity * antiGravityScale * Time.deltaTime;

        var world = ObiColliderWorld.GetInstance();
        foreach (Oni.Contact contact in e.contacts)
        {
            // this one is an actual collision:
            if (contact.distance < 0.01)
            {
                ObiColliderBase col = world.colliderHandles[contact.bodyB].owner;

                // if this is the antigravity trigger;
                if (col != null && col.gameObject.GetComponent<Collider>() == antiGravityCollider)
                {
                    // get the index of the particle involved in the contact:
                    int particleIndex = solver.simplices[contact.bodyA];

                    // set the particle velocity:
                    solver.velocities[particleIndex] += antiGravityAccel;
                    Debug.DrawLine(solver.positions[particleIndex], contact.pointB, Color.green);
                    collisionPositions.Add(solver.positions[particleIndex]);
                }
            }
        }
    }
    private void OnDrawGizmos()
    {
        foreach (var position in collisionPositions)
        {
            Gizmos.DrawSphere(position, 0.1f);
        }
        collisionPositions.Clear();
    }
}

Why is the colission not correct? I made sure the solver and rope pos, rot, and scale are 0.
Reply
#2
Hi there,

The code looks fine. Tested it myself in an empty project and it works correctly too. Can you give more details about your setup?
Reply
#3
(12-08-2021, 02:16 PM)josemendez Wrote: Hi there,

The code looks fine. Tested it myself in an empty project and it works correctly too. Can you give more details about your setup?
Simple rope w ~200 particles. No curser used. Two particle attachments, one in each end. Created from a blueprint made of 4 points on the curve (two start/end, two in the middle). All constraints default, except self and surface based collision turned on. Using the custom updater we discussed earlier (that I need for networking), but currently testing without networking;
Code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;

namespace Obi
{
    /// <summary>
    /// Updater class that will perform simulation during FixedUpdate(). This is the most physically correct updater,
    /// and the one to be used in most cases. Also allows to perform substepping, greatly improving convergence.

    [AddComponentMenu("Physics/Obi/Obi Custom Fixed Updater", 801)]
    [ExecuteInEditMode]
    public class ObiCustomFixedUpdater : ObiUpdater
    {
        /// <summary>
        /// Each FixedUpdate() call will be divided into several substeps. Performing more substeps will greatly improve the accuracy/convergence speed of the simulation.
        /// Increasing the amount of substeps is more effective than increasing the amount of constraint iterations.
        /// </summary>
        [Tooltip("Amount of substeps performed per FixedUpdate. Increasing the amount of substeps greatly improves accuracy and convergence speed.")]
        public int substeps = 4;
        public bool solve = true;

        private float accumulatedTime;

        private void OnValidate()
        {
            substeps = Mathf.Max(1, substeps);
        }

        private void Awake()
        {
            accumulatedTime = 0;
        }

        private void OnDisable()
        {
            Physics.autoSimulation = false;
        }

        private void FixedUpdate()
        {
            if (solve)
            {
                ObiProfiler.EnableProfiler();

                BeginStep(Time.fixedDeltaTime);

                float substepDelta = Time.fixedDeltaTime / (float)substeps;

                // Divide the step into multiple smaller substeps:
                for (int i = 0; i < substeps; ++i)
                    Substep(Time.fixedDeltaTime, substepDelta, substeps - i);

                EndStep(substepDelta);

                ObiProfiler.DisableProfiler();

                accumulatedTime -= Time.fixedDeltaTime;
            }
        }

        private void Update()
        {
            ObiProfiler.EnableProfiler();
            Interpolate(Time.fixedDeltaTime, accumulatedTime);
            ObiProfiler.DisableProfiler();

            if (solve)
                accumulatedTime += Time.deltaTime;
        }
    }
}

Edit; I noticed the further down the rope i test colission, the bigger the indexing error (in the photo its a few particles, but later down the rope its more than 20 indecies off);

[Image: DIFYD.png]

Edit 2:
Just made a new scene in which it works perfectly - I tried with the same custom updater and the same rope blueprint - still works.
Then tried recreating the rope with a new rope component and solver in the original scene, and the issue still persists.

The only difference is the original scene has a bunch of obi colliders everywhere and some spherical force zones.

Edit 3:
Just redid the whole rope again and now it works. Have no idea where this indexing error came from.
The old one is still broken, and everything looks to be identical component wise;
[Image: DIGLB.png]
Reply
#4
(12-08-2021, 02:16 PM)josemendez Wrote: Hi there,

The code looks fine. Tested it myself in an empty project and it works correctly too. Can you give more details about your setup?

Edit 4;

I just got the bug again. It appeared after a unity crash - now all the indices are skewed again on the new rope I created which has been working for a few hours.

Edit5;

Reloading scene/restarting unity has no effect. And tried rebuilding rope from scratch without luck. This is really frustrating.
Reply
#5
I've never had any similar reports, or experienced this myself. The indices passed to the contacts you get in the callback are the exact same ones used for collision response (in fact, the engine simply exposes the exact same contact list used for solving physics) so if the indices are wrong, collisions shouldn't work either.

Would it be possible for you to provide a repro scene/project so that I can take a closer look? If so, send it to support(at)virtualmethodstudio.com

Edit: just noticed this bit:

Quote:All constraints default, except self and surface based collision turned on

If you're using surface collisions, then your code won't work! It's written for particles, not simplices. As explained in the manual, surface collisions are simplex-based, not particle-based. I was assuming you were using particles for collision detection. Copypasting form the docs:

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

"Contacts will contain slightly different information for actors using surface collisions, this is worth noting if you're using contact callbacks."

The contact callbacks page goes into details regarding how to deal with simplices:
http://obi.virtualmethodstudio.com/manua...sions.html

Retrieving the particle involved in a contact

In Obi 6.X, collision detection is simplex-based. A simplex can be a triangle (3 particles), an edge (2 particles) or a single particle. The solver.simplices array stores simplices as particle index tuples (see surface collisions for more info). To retrieve the solver indices of the particles in a simplex, you can do the following:

Code:
// retrieve the offset and size of the simplex in the solver.simplices array:
int simplexStart = solver.simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);

// starting at simplexStart, iterate over all particles in the simplex:
for (int i = 0; i < simplexSize; ++i)
{
    int particleIndex = solver.simplices[simplexStart + i];

    // do something with each particle, for instance get its position:
    var position = solver.positions[particleIndex];
}

In case there isn't any actor using surface collisions in your solver, all simplices will have size 1 and the code above can be simplified to:

Code:
// get the particle index directly, as all simplices are guaranteed to have size 1:
int particleIndex = solver.simplices[contact.bodyA];

// do something with the particle, for instance get its position:
var position = solver.positions[particleIndex];

You are using surface contacts, so your code can't assume each simplex is composed of 1 particle. In particular, rope simplices have 2 particles each. So you *must* use the first snippet, the one that iterates trough all particles in each simplex. You're currently using simplex indices as if they were particle indices, so your values will be off by a factor of roughly 2 and this "offset" in the indices will increase the further away down the rope you test collisions, just like you describe.
Reply
#6
(13-08-2021, 08:16 AM)josemendez Wrote: I've never had any similar reports, or experienced this myself. The indices passed to the contacts you get in the callback are the exact same ones used for collision response (in fact, the engine simply exposes the exact same contact list used for solving physics) so if the indices are wrong, collisions shouldn't work either.

Would it be possible for you to provide a repro scene/project so that I can take a closer look? If so, send it to support(at)virtualmethodstudio.com

Edit: just noticed this bit:


If you're using surface collisions, then your code won't work! It's written for particles, not simplices. As explained in the manual, surface collisions are simplex-based, not particle-based. I was assuming you were using particles for collision detection. Copypasting form the docs:

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

"Contacts will contain slightly different information for actors using surface collisions, this is worth noting if you're using contact callbacks."

The contact callbacks page goes into details regarding how to deal with simplices:
http://obi.virtualmethodstudio.com/manua...sions.html

Retrieving the particle involved in a contact

In Obi 6.X, collision detection is simplex-based. A simplex can be a triangle (3 particles), an edge (2 particles) or a single particle. The solver.simplices array stores simplices as particle index tuples (see surface collisions for more info). To retrieve the solver indices of the particles in a simplex, you can do the following:

Code:
// retrieve the offset and size of the simplex in the solver.simplices array:
int simplexStart = solver.simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);

// starting at simplexStart, iterate over all particles in the simplex:
for (int i = 0; i < simplexSize; ++i)
{
    int particleIndex = solver.simplices[simplexStart + i];

    // do something with each particle, for instance get its position:
    var position = solver.positions[particleIndex];
}

In case there isn't any actor using surface collisions in your solver, all simplices will have size 1 and the code above can be simplified to:

Code:
// get the particle index directly, as all simplices are guaranteed to have size 1:
int particleIndex = solver.simplices[contact.bodyA];

// do something with the particle, for instance get its position:
var position = solver.positions[particleIndex];

You are using surface contacts, so your code can't assume each simplex is composed of 1 particle. In particular, rope simplices have 2 particles each. So you *must* use the first snippet, the one that iterates trough all particles in each simplex. You're currently using simplex indices as if they were particle indices, so your values will be off by a factor of roughly 2 and this "offset" in the indices will increase the further away down the rope you test collisions, just like you describe.
Thank you so much!
Sorry I never discovered this page after upgrading to 6.0. If I can give any feedback it would be to have a more dedicated page for new features of different obi versions linking to these new documentation sites. Right now I can only find info about new features on the blog Sonrisa

*EDIT:*
Also a side question;
Is it possible to "subscribe" to an external force per particle? Like lets say I want particles in water to experience "water-current" forces while the above water should experience "wind". I know I could do this manually, but I like the built-in force system as it's easy to manipulate.
Reply