Particle Shifting

One effect that is based on get statements is particle shifting. This method is widely used for all kinds of effects, for example foam generation from traditional emitters. With grid fluids this special way of creating spray is obsolete, because it is already implemented through various emitter types. 

The idea behind particle shifting is to find a certain condition when particles will be shifted from emitter A to emitter B. This makes it possible to render one emitter as a fluid and the second one as spray, to give an example. The advantage is that the shift is completely seamless and there are no visible gaps between the particles. The transaction is normally triggered by comparing a parameter against a threshold value. It is very important that both emitters share equal resolutions, otherwise the particles might explode. Another prerequesite is that the receiving emitter’s speed is 0.0, since it should not produce any new particles.

Particle shifting can also be done with the "Container" emitter and "Filter" daemon. With this built-in and ready-to-use combination you can achieve the same effect, but sometimes it is better to implement the transition from one emitter to another with a script: 

 

# Type this part to Simulation Events > Scene > ScenePre
# Initialize the container emitter before starting the simulation

source_emitter    = scene.get_PB_Emitter("Source")
source_resolution = source_emitter.getParameter("Resolution")
container_emitter = scene.get_PB_Emitter("Container")

container_emitter.setParameter("Interpolation", "Local")
container_emitter.setParameter("Resolution",source_resolution)
container_emitter.setParameter("Speed", 0.0)

# Write this part to Simulation Events > Steps > StepsPre

velocity_threshold = 3.0
source_emitter     = scene.get_PB_Emitter("Source")
container_emitter  = scene.get_PB_Emitter("Container")
source_particle    = source_emitter.getFirstParticle()

while (source_particle):
    source_particle_vel       = source_particle.getVelocity()
    source_particle_magnitude = source_particle_vel.module()
    source_particle_id        = source_particle.getId()

    if (source_particle_magnitude >= velocity_threshold):
        source_particle_pos = source_particle.getPosition()
        container_emitter.addParticle(source_particle_pos,source_particle_vel)
       	source_emitter.removeParticle(source_particle_id)

    source_particle = source_particle.getNextParticle()

 

Now, what is going on here? The basic structure and syntax should already be familiar, though there are few new functions. The very first part initializes the container emitter. You may remember that it is crucial that both emitters share equal resolution and the container’s speed must be 0.0. Equalizing the resolution is a pretty easy task, but it is recommended to change interpolation to local, as you perform a change of resolution. The script just reads out the resolution value from the source emitter and uses the result for the container. Resetting speed to 0.0 is even simpler, because you just have to set the parameter to the desired value. Here it would also be possible to insert an “if”-condition:

 

container_emitter_speed = container_emitter.getParameter("Speed")

if (container_emitter_speed != 0.0):
 container_emitter.setParameter("Speed", 0.0)

 

This means that if the container emitter’s speed is not 0.0 then you need to reset it. This query is not really necessary here, but similar constructions might be useful for other tasks where you have to reset speed or other parameters, or limit them to a certain value. The second part of the script is executed during a time step. First you have to find a certain threshold value. This threshold triggers the particle’s transition to the container emitter. The next step is a basic loop through all of the source emitter’s particles. As long as there are particles, the script constantly checks whether the velocity_threshold value is exceeded or not. For this purpose the script does not check against each individual value of the velocity vector, because this would not lead to reasonable results. 

Here, the magnitude of the velocity vector is used. The magnitude is not a vector, but a single floating number, which is much easier to handle and can be extracted with:

 

source_particle_velocity  = source_particle.getVelocity()
source_velocity_magnitude = source_particle_velocity.module()

 

The vector’s magnitude is then compared with the threshold value and, if the condition is true, the script reads out the affected particle’s ID to identify it. Another important parameter is the particle’s position here, because it is necessary to restore it to the same position when the particle has been shifted to the container. In the next step, the script removes the particle with the appropriate ID and applies it to the second emitter with the original position and velocity. That is necessary to keep the fluid stable. The threshold value could also be any other parameter, even a value from a third emitter, or even the velocity of an object – you can establish any dependency.