Obi Official Forum
Help How to know when all the particles are sleeping so I can de-activate the ObiEmitter - Printable Version

+- Obi Official Forum (https://obi.virtualmethodstudio.com/forum)
+-- Forum: Obi Users Category (https://obi.virtualmethodstudio.com/forum/forum-1.html)
+--- Forum: Obi Fluid (https://obi.virtualmethodstudio.com/forum/forum-3.html)
+--- Thread: Help How to know when all the particles are sleeping so I can de-activate the ObiEmitter (/thread-2167.html)



How to know when all the particles are sleeping so I can de-activate the ObiEmitter - facaelectrica - 15-04-2020

Hi,

I'm using the ObiFluid to simulate liquid pooling and I would like to know when all the particles have stopped moving so I can 'freeze' the simulation on the ObjEmitter since it won't be interacted with anymore once all particles have stopped.

I noticed that if I manually disable the Obi Emitter modifier I see the framerate recover but otherwise, even if the particles aren't moving anymore, it seems to still be calculating.

Is there a way to check if all particles of a specific Obi Emitter are sleeping and disable it?


RE: How to know when all the particles are sleeping so I can de-activate the ObiEmitter - josemendez - 15-04-2020

(15-04-2020, 12:50 AM)facaelectrica Wrote: Hi,

I'm using the ObiFluid to simulate liquid pooling and I would like to know when all the particles have stopped moving so I can 'freeze' the simulation on the ObjEmitter since it won't be interacted with anymore once all particles have stopped.

I noticed that if I manually disable the Obi Emitter modifier I see the framerate recover but otherwise, even if the particles aren't moving anymore, it seems to still be calculating.

Is there a way to check if all particles of a specific Obi Emitter are sleeping and disable it?

Hi!

The component that actually performs the simulation is the solver, so that's what you want to deactivate, not the emitter. See:
http://obi.virtualmethodstudio.com/tutorials/obisolver.html

Regarding the sleep threshold, quote from the docs:

Quote:Sleep threshold: Any particle with a kinetic energy below this value will be freezed in place. This is useful when you don´t want minuscule variations in velocity or force to perturb an actor, making it look like its jittering or moving very slowly.

It is important to note that sleeping particles aren't skipped when asleep, it is just that their velocity is not used to update their position. This way, tiny velocities won't move the particle around. This is similar to how articulated bodies in physx can't have their individual links sleeping, only the entire articulation can go to sleep once all links have their kinetic energy below a threshold.

Kinetic energy is just 1/2*mass*sqr(velocity). Because we want the sleep threshold to work the same regardless of particle mass, internally we ignore mass in the formula, leaving: 1/2*sqr(velocity).

So simply iterate over all particles, checking if half of the squared magnitude of their velocity is below the sleep threshold. However, determining when to re-enable the solver isn't trivial at all: the simulation is no longer being updated, so you can't rely on particle velocities or the simulation internals for this. Depending on your use case this might not be an issue (maybe you don't ever need to re-enable the solver after all particles stop moving).


RE: How to know when all the particles are sleeping so I can de-activate the ObiEmitter - facaelectrica - 15-04-2020

@Jose Thank you for the quick reply which actually raises a few more questions!

Here is an overview of my setup, to make sure I'm using the plugin correctly.

Every time a character gets shot or stabbed, I'm creating a blood emitter that fires once. 
The particles interact with the environment and NOT the characters.
Once the blood has finished pooling, it will no longer interact with the surface and stays on the scene until the level is reloaded.
Example of what I have currently:
https://www.dropbox.com/s/m6w9ve2t3eorkcb/FluidSim_06.mp4?dl=0

In terms of setup, I'm using a single ObiSolver and I parent each ObiEmitter that I instance (no matter what character is using it).
I also instance a transform parented to the character (from where I want the blood to spurt from) that updates the ObiEmitter transform position and rotation so I don't need to have the emitter or solver parented to the character.

Each emitter fires 1000 particles, and unless I disable the ObiEmitter component after they have stopped moving, I see a big impact in the framerate once I have two emitters. But if I disable the ObjEmitter the framerate stays super stable.

1.  Is this a good setup overall? I like the idea of a single solver managing all the Emitters but you seem to suggest I should have one Solver per Emitter?
2.  I couldn't figure out a way to have the emitter parented to the actually skinned character. If I parent the ObjSolver to a character the simulation is all messed up. It looks like the solver has to be static? That is why I'm manually updating the ObjEmitter position and rotation based on a gameObject parented to the skinned character in Update(), is there a better way?

As for my initial issue:
3. You're suggesting that I disable the solver, but this would mean that I have to create one solver per emitter, right? But it all seems to work when I just disable the ObjEmitter component. Could I have the solver give me information on a single Emitter? 

If moving forward with your proposal, could you guide me on how I would go about "iterate over all particles, checking if half of the squared magnitude of their velocity is below the sleep threshold."? Is there a specific method I should be calling?

And like you mentioned, once stopped, I never re-enable the particles, only once the scene has reset and we do it all over again.

Thanks for your time. Also if you have other thoughts on how to deal with the scenario I have please let me know. I just set it up so I'm happy to make it more friendly for your system.

P.S. For the fluid rendering I'm using the ObiSimpleFluidRender in the MainCam, with the simple fluid material, is that ok? The manual only mentions the other more complex component (ObiFuidRenderer) for rendering and I prefer the look of this one (also wondering if it's better performance wise).


RE: How to know when all the particles are sleeping so I can de-activate the ObiEmitter - facaelectrica - 16-04-2020

(15-04-2020, 10:08 AM)josemendez Wrote: Kinetic energy is just 1/2*mass*sqr(velocity). Because we want the sleep threshold to work the same regardless of particle mass, internally we ignore mass in the formula, leaving: 1/2*sqr(velocity).

So simply iterate over all particles, checking if half of the squared magnitude of their velocity is below the sleep threshold.

Following this, I tried to get a function going, but I'm struggling figuring out how to access the sleep threshold value. Code below:

Code:
    bool CheckIfParticlesAreAsleep(ObiEmitter actor, ObiSolver objSolver) {        
        bool allAsleep = true;

        float sleepTreshold = 0.0001f; //objSolver.sleepTreshold;    //How do I get this value?

        for (int i = 0; i < actor.solverIndices.Length; ++i) {
            Vector3 velocity = objSolver.velocities[i];
            float particleKineticEnergy = 0.5f * Vector3.SqrMagnitude(velocity);

            if (particleKineticEnergy > sleepTreshold) {
                allAsleep = false;
                break;
            }
        }

        return allAsleep;
    }



RE: How to know when all the particles are sleeping so I can de-activate the ObiEmitter - josemendez - 17-04-2020

solverObj.parameters.sleepThreshold;


RE: How to know when all the particles are sleeping so I can de-activate the ObiEmitter - facaelectrica - 17-04-2020

(17-04-2020, 06:34 AM)josemendez Wrote: solverObj.parameters.sleepThreshold;

Thanks!
I've also noticed that when I reload the same scene, the next time I spawn an obiEmitter, the particles will not detect any of the ObiColliders.
The only workaround I have so far is to re-assign the MeshCollider for each ObiCollider on SceneLoad.
Is this expected? What could be causing it?

Also, if for some reason a particle falls from the ground surface into infinite, is there a way to 'force' it to stay at 0, or better yet, to kill it and add it back to the Emitter to spawn again?
Would it be something like this?
Code:
            int solverIndex = bloodEmitter.solverIndices[i];        
            Vector3 particlePos = bloodEmitter.GetParticlePosition(solverIndex);

            //Check if the particle is 'falling' off the ground
            if (particlePos.y < 0f) {
                bloodEmitter.TeleportParticle(solverIndex, bloodEmitter.transform.position);
            }