Obi Official Forum

Full Version: Is there a way to kill fluid particles on contact with a different fluid?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I would like to have one fluid dissolve almost immediately after coming in contact with a different fluid rather than the two of them mixing, ideally with a short delay of around 0.5-1 second so it looks a bit more like they're mixing rather than the particles being killed.

Is this possible? I suppose this would require creating a script that talks to individual particles, which are probably not exposed by the backend.

If it's not possible, any ideas on how to achieve a similar effect? The scenario is mixing a small amount of very high resolution fluid into a large volume of low resolution fluid. The highres fluid is taken from a bottle with a dropper that has an emitter and added to the pool of lowres fluid, which needs to have basically no effect on the volume of that lowres fluid.

If I don't destroy the highres fluid on contact with other fluids I get nothing but problems:

- Even though the resolution ratio is very high and the amount of added fluid is very low, there still is a disproportionately large increase in total volume

- I'm already hitting the particle budget on my machine, so the highres fluid staying around creates a lasting performance hit that doesn't serve any purpose.

- Since the highres fluid is being sourced from a different emitter pool in the bottle, the script that controls the emitter on the dropper also needs to selectively destroy particles from the bottle pool to account for the fluid that is now stored as a value in the script. This means I run into the exact same issue again, I either have to destroy the dropper particles on contact with the bottle particles or the other way around. The only way to fix that I can see would be to remove the dropper emitter and instead create a script that moves the emitter from the bottle to the dropper after a few seconds and the sets the emission speed to 0, which is not exactly ideal either.
(04-05-2022, 09:45 AM)locque Wrote: [ -> ]I would like to have one fluid dissolve almost immediately after coming in contact with a different fluid rather than the two of them mixing, ideally with a short delay of around 0.5-1 second so it looks a bit more like they're mixing rather than the particles being killed.

Is this possible? I suppose this would require creating a script that talks to individual particles, which are probably not exposed by the backend.

Fluid particles don't collide with other fluid particles. They interact with each other trough density constraints., so there's no way to know when two fluid particles contact each other, it's not an instantaneous event unlike collision constraints.

Internally, density constraints are stored using a grid structure that's not exposed trough the API. Even if it was, the only way to access it efficiently would be using jobs since the neighborhood of each particle can be rather large.

(04-05-2022, 09:45 AM)locque Wrote: [ -> ]If it's not possible, any ideas on how to achieve a similar effect? The scenario is mixing a small amount of very high resolution fluid into a large volume of low resolution fluid. The highres fluid is taken from a bottle with a dropper that has an emitter and added to the pool of lowres fluid, which needs to have basically no effect on the volume of that lowres fluid.

You could simply delete fluid particles whose userData has reached a given threshold. For instance, if your high-fluid particles carry a userData value of (1,0,0,0) and the low-res carries (0,0,0,0), once they're completely mixed both would have a value of (0.5,0,0,0). You could detect fully "dissolved" high-res particles by checking their userData and then kill them.

Another option: Since the high-res fluid shouldn't affect the volume of the low-res fluid, maybe this could be done using advection? You can use solver.implementation.InterpolateDiffuseProperties() to get interpolated fluid properties at any position. See ParticleAdvector.cs for a use case that uses interpolated fluid velocities to drive regular Unity particle system (this is how foam is done in the FluidFoam sample scene).

If you want to pass properties from the advected particles back to the fluid (such as color), you'd need to modify the source code of InterpolateDiffuseProperties() for the Burst backend, to allow writing data the other way around (advected particles->fluid particles).

kind regards,
Thanks for the help once again.

I'm trying a different approach for now, I'll try to actually kill the particles in contact with the dropper tip while applying suction.

I'm stuck already though, I can't get the index of those particles. I followed your instructions from the manual page about the trigger volume that reverses the gravity of the particles touching it, but I'm getting different results.


Code:
    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 collider is tagged as "zero gravity":
                if (col != null && col.gameObject.CompareTag("zeroGravity"))
                {
                    // get the index of the particle involved in the contact:
                    int particleIndex = solver.simplices[contact.bodyA];

                    // set the particle velocity:
                    solver.velocities[particleIndex] += antiGravityAccel;
                }
            }
        }
    }
}



I'm getting a ton of collisions from the solver, but apparently all of them are untagged even though my dropper has tags on the rigidbody and also on the trigger colllider. I also tried to directly compare the colliderbase gameobject reference with the dropper gameobject that has the trigger collider on it, but that never produces a match either. It looks like some contacts are simply missing from collisioneventargs.
(05-05-2022, 10:56 AM)locque Wrote: [ -> ]I'm getting a ton of collisions from the solver, but apparently all of them are untagged even though my dropper has tags on the rigidbody and also on the trigger colllider. I also tried to directly compare the colliderbase gameobject reference with the dropper gameobject that has the trigger collider on it, but that never produces a match either.

Hi!

Just tried this script on a scene with an emitter and a few cubes, works properly for me. The cube that's tagged appears in the contact list as many times as particles inside of it.

Make sure that:
- The name of the tag actually matches string name you pass to CompareTag()!
- The colliders involved in this have a matching ObiCollider component, otherwise they won't show up in the list (even if they're trigger colliders).

(05-05-2022, 10:56 AM)locque Wrote: [ -> ]It looks like some contacts are simply missing from collisioneventargs.

That's not possible, since the list passed to collisioneventargs is the exact same one used internally by the engine to resolve collisions. If there's a collision taking place, it *must* appear in the list.
Quote:- The colliders involved in this have a matching ObiCollider component, otherwise they won't show up in the list (even if they're trigger colliders).

Thanks, I made a stupid mistake where I mixed up the regular and obi colliders on my dropper. Works great now! :)