Particle Motion Transfer to Scene Objects

This simple tutorial provides the basics of moving scene objects using Particle Flow.
The objects can be anything from lights to helpers or mesh objects.

In this example, we will move a large number of Atmospheric Gizmos with a Fire Effect assigned to simulate a steaming Teapot.

Click here to download the MAX Scene (56K, 3dsmax 5.1 with PFlow, 3dsmax 6 or higher)

Natural Language - Setup
Particle View Setup

Script

on ChannelsUsed pCont do
(
    pCont.useTM = true
    pCont.useAge = true
)

on Init pCont do
(
    global My_Athmospheric_Gizmos_01 = $SphereGizmo*
    My_Athmospheric_Gizmos_01.pos = [0,0,-100000]
)

on Proceed pCont do
(
    partcount = pCont.NumParticles()
    count = amin #(partcount,My_Athmospheric_Gizmos_01.count)
    for i in 1 to count do
    (
        pCont.particleIndex = i
        My_Athmospheric_Gizmos_01[i].transform = pCont.ParticleTM
        My_Athmospheric_Gizmos_01[i].radius = 10 + pCont.ParticleAge*2
    )
)


on Release pCont do
(

)

 

Results


The following QuickTime animation (6MB) shows the resulting animation (rendered in Brazil r/s, still image below rendered in Scanline)

Step-By-Step Comments

on ChannelsUsed pCont do
(

The ChannelsUsed handler defines the channels to be used by the Script Operator - you cannot get or set particle related values from the particle container without specifying which properties you need access to. This way, Particle Flow does not have to provide the Script Operator with all possible channels (and there can be an arbitrary number of channels in Particle Flow) but only with those that are actually needed. This conserves memory!
The parameter pCont contains  the Particle Container.

pCont.useTM = true

We want to copu the complete transformation of the particle to the scene object, so we will need access to that channel!.

pCont.useAge = true

To get an even nicer effect, we will want to read the age of the particle and assign it to the gizmos' radius property to make them grow over time!

)

on Init pCont do
(

The Init  handler is used to initialize the Script Operator. 
The parameter pCont contains  the Particle Container.

global My_Athmospheric_Gizmos_01 = $SphereGizmo*

We define a global variable which will contain an array of the scene objects to be driven by the particles.
In this case, we collect all SphereGizmos from the scene using their common base name.

My_Athmospheric_Gizmos_01.pos = [0,0,-100000]

To remove the gizmos from the scene in the first frames of the animation, we move them away from the camera field of view, in this case way down.

)

on Proceed pCont do
(

The Proceed  handler is called every time the Script Operator is evaluated by Particle Flow. 
It contains the actual body of the script.
The parameter pCont contains  the Particle Container which contains all particles the Operator is applied to.


partcount = pCont.NumParticles()

First we read the number of particles in the current Event. There can be 0 or millions of particles in the event.

count = amin #(partcount,My_Athmospheric_Gizmos_01.count)

Then we compare the number of particles to the number of collected gizmos.
The function amin returns the smallest value in the array.
The same could be expressed with
if partcount < My_Athmospheric_Gizmos_01.count then count = partcount else count = My_Athmospheric_Gizmos_01.count
but you must admit the first version looks cooler! Also, it would work with more than two values if necessary, always picking the smallest value.

Why do we need this?
Because we have a limited number of particles in the beginning of the animation, less than the number of gizmos.
After a while, the number of particles will be higher than the available gizmos. In both cases, we want to build "pairs" of gizmo+particle,
and some of the particles or gizmos might have no corresponding partner at certain parts of the animation.
Knowing the lowest count of the two sets of objects gives us the number of possible pairs we can process!

for i in 1 to count do
(

Now we will repeat the following code as many times as there are possible particle+gizmo pairs in the scene...

pCont.particleIndex = i

In order to read data from a particle, we must make it the current one.
To do so, we assign the index i to the particleIndex property of the particleContainer of the current event.
After this, any particle-related queries or assignments will be performed on the i-th particle only!

My_Athmospheric_Gizmos_01[i].transform = pCont.ParticleTM

Now we can assign the transformation matrix of the current i-th particle to the i-th gizmo in the array.
Note that because of our smart amin test above, i is guaranteed smaller or equal to both the available particle and gizmo counts...

My_Athmospheric_Gizmos_01[i].radius = 10 + pCont.ParticleAge*2

Finally, we change the radius of the i-th gizmo to 10 units plus twice the age of the i-th particle.
10 is the minumum size of a gizmo (when particleAge is 0), at age of 10 the gizmo's radius will be 30 units etc.
You can play with different values to get different cloud behaviours.

)
)
 

on Release pCont do
(

)

Copyright 2004 by Borislav 'Bobo' Petrov.