What I wanted to acheive with the particle flow is to have a set of thirty spiders that move around a little landscape at varying speeds. I spiced this up a little with a gate through which some spiders can "escape" and also some sphere's as obstacles for the critters to avoid. So that being said, lets step through the pflow.
Spiders (source event)
The main thing here is that the viewport display was set to 100% as we really want to see all of our spiders. Also the integration step for both the viewport and render is set to Half Frame , this helps with the overall flow accuracy.
Render
Set to geometry, nothing fancy going on here....
Cache
Set to viewport/render, to record each integration step and it also has the max memmory limit
Collision
This sets up global collision detection for the landscape boundary wall. On collide the spiders bounce
Spiders_Birth (birth event)
Birth
All thirty spiders are born on frame zero
Position Object
The spiders are born randomly on the GroundPlane mesh. They are further limited to only the selected verticies of this object, and a minimum spacing of sixteen units.
Speed
The spiders are given a random horizontal initial speed to get them on their way
Age Test
The spiders are tested to be greater than age zero, as they all pass their on to the next event.
Spiders_Walk (main event)
Material Static 01
All thirty spiders are born on frame zero
Force Gravity
Three gravities are used here. The first two are negative and are used to push the spiders away from the sphericalobstacles in their little pen. The third gravity is used to pull the spiders towards the exit and onwards towards the dark tunnel.
Keep Apart
This operator is used to keep the spiders from colliding, in some cases however it fails. I believe this is caused by the integration step value i.e. with a lower integration step the spiders would avoid each other more effectively
Speed by Surface
The speed by surface operator makes the spiders follow the surface of the GroundPlane mesh
Rotation
This Rotation operator is set to Speed Follow, this aligns the spider with the direction it is travelling in.
Store Speed Script
This script stores the speed of the spider in the particles float channel, this is used in the next operator and is explained in depth later in the tutorial
Force Wind
This force operator is used to apply the wind force to each spider. The wind is set to have no actual strength, however it does have Turbulence, and this makes the spiders walk in a slighly more erratic manner. However it also had the undesirable effect of making very slow spiders walk too erraticly. To get arround this problem the float value stored in the previous script operator is used here via script wiring to control the force ops influence value. So the previously stored speed is used as a multiplier of the effect of the wind, meaning slow spiders have a low multiplier and walk in a pretty straight line, whilt faster spiders are much more erratic.
Spider Shape Script
This is the meat and potatoes of the pflow and is explained in depth further on in the tutorial, but it basicly works out the spiders speed and tailors the animation rate to that speed.
Display
Displays each spider particle as a mesh.
The Scripts
Here are the two scripts that open up some of the hiden power of particle flow. The first is used in conjunction with the force wind operator in the Spiders Walk event. As explained above it stores the current particle speed value which is used by the script wiring feature of the force operator to allow explicit controll over the strength of the force. Here's the script and bellow is a step by step explanation of its functionality
Store Speed Script
on ChannelsUsed pCont do
(
pCont.useSpeed = true
pCont.useFloat = true
)
on Init pCont do
(
)
on Proceed pCont do
(
count = pCont.NumParticles()
for cx in 1 to count do
( pCont.particleIndex = cx
p2speed = pCont.particlespeed pvel = sqrt (p2speed.x*p2speed.x+p2speed.y*p2speed.y+p2speed.z*p2speed.z)
pCont.particleFloat = (pvel*300)
)
)
on Release pCont do ( )
How does it work?
on ChannelsUsed pCont do
When you want to use Channels in a particle flow script, you have to activate them first. This is used so that particle flow does not have to have all channels available to all particle at all times, which would be a massive waste of memmory (there are a lot of channels). In this case we want to put a value into each Particle Float Channel so we need this to be active. Plus we also need the particle speed channel to be active so we can actually get the speed in the first place. So as you can see, both channels are activated here.
(
pCont.useSpeed = true
pCont.useFloat = true
)
on Init pCont do
Sometimes when you start a script you may want to setup some variables or create helpers etc, this is done here. In this case I haven't, so this area is left blank.
(
)
on Proceed pCont do
This is where the main script takes place
(
count = pCont.NumParticles()
This sets the variable count to the number of particles in the simulation
for cx in 1 to count do
This line then uses the value of count as the basis of a for/loop . This then runs the subroutine below for each particle in the system
(
pCont.particleIndex = cx
Sets the particle index to the same value as cx i.e. from 1 to 30 in this case as the for/loop progresses
p2speed = pCont.particlespeed
Get the current particles speed vector and put it into the p2speed variable
pvel = sqrt (p2speed.x*p2speed.x+p2speed.y*p2speed.y+p2speed.z*p2speed.z)
This converts the vector in p2speed into the float value that is needed by the Force Wind operator and stores it in pvel
pCont.particleFloat = (pvel*300)
This multiplies the value in pvel by 300 and stores it in the particles float channel. The value is multiplied by three hundred to make it into a value that is suitable for the influence field of the Force operator
)
)
on Release pCont do
pCont.particleFloat = (pvel*300)
Again, this area can be used for any script cleanup that you may need. In this case it is not used
(
)
The Spider Shape Script
on ChannelsUsed pCont do
(
pCont.useShape = true
pCont.useTM = true
pCont.useSpeed = true
pCont.useMatrix = true
)
on Init pCont do ( )
on Proceed pCont do
(
count = pCont.NumParticles()
for i = 1 to count do
(
pCont.particleIndex = i
if pCont.particleNew == true do
(
raty = pCont.particleMatrix raty.row1.x = random 0 12
pCont.particleMatrix = raty
)
pspeed = pCont.particlespeed pvel = sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)
maty = pCont.particleMatrix
ism = $'Spiders'.getIntegrationStep()
maty.row1.x = (maty.row1.x + ((pvel * 350) * ism))
pCont.particleMatrix = maty
$SPD_Master.Custom_Attributes.Ease_Curve_Value = maty.row1.x
new_mesh = Editable_Mesh()
$Object01.mesh = $SPD_MSH_Skin.mesh
$Object01.transform = pCont.particleTM
addModifier $Object01 (XForm ()) ui:off $Object01.modifiers[#XForm].gizmo.rotation += quat 0 0 1 0
pCont.particleShape = $Object01.mesh
delete $Object01
)
)
on Release pCont do ( )
How does it work?
on ChannelsUsed pCont do
Here the channels needed for this script are activated. The shape channel is used to set the polygonal mesh shape of each particle. The TM channel is used to orient the mesh to the correct position and rotation for each particle. The speed channel is used to evaluate the speed of each particle from frame to frame and the Matrix channel is used the store the animation offset for each particle from frame to frame. Idealy the float channel would have been used for this (for simplicities sake) but this channel has already been used in the Store Speed Script
(
pCont.useShape = true
pCont.useTM = true
pCont.useSpeed = true
pCont.useMatrix = true
)
on Init pCont do
The initialisation portion of the script. I do my initialisation elsewhere so in this case it is not used.
(
)
on Proceed pCont do
This is where the main script takes place
(
count = pCont.NumParticles()
This sets the variable count to the number of particles in the simulation
for i = 1 to count do
This line then uses the value of count as the basis of a for/loop . This then runs the subroutine below for each particle in the system
(
pCont.particleIndex = i
Sets the particle index to the same value as i i.e. from 1 to 30 in this case as the for/loop progresses
if pCont.particleNew == true do
If the particle is new to this event the script carries out the following initialisation routine to create a random animation offset between one and twelve. This value is then stored in the x value of the first row of the particles Matrix channel for later use.
(
raty = pCont.particleMatrix
The particle matrix channel value is read into the variable raty . This is because it cannot be manipulated directly as you can with the other value types such as integers or floats. In this case you have to read the value into a variable, manipulate it via the variable and then feed that value back into the matrix channel.
raty.row1.x = random 0 12
This sets the value of raty.row1.x to a random value from one to twelve (the matrix is made up of 16 float values, with x y and z values in each of the four rows. So in this case raty.row1.x could be said to be the "first" value in the matrix.
pCont.particleMatrix = raty
The now modified matrix is stored back in the Particle Matrix Channel for later use
)
pspeed = pCont.particlespeed
The speed vector of the particle is stored in the variable pspeed
pvel = sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)
The vector value in pspeed is converted into a float via this expression and is then stored in the variable pvel
maty = pCont.particleMatrix
The previously stored Matrix Channel value is read and stored in the variable maty
ism = $'Spiders'.getIntegrationStep()
The systems integration step is stored in the variable ism
maty.row1.x = (maty.row1.x + ((pvel * 350) * ism))
The recovered animation offset value is made to equal itself plus the modified particle speed. The particle speed is modified by multiplying it first by 350, this basicly adjusts the spiders walk speed so it is in proportion with the actual particles movement. Then the particle speed value is further modified by multiplying it by the integration step value. In this case the value is .5 and has the result of dividing the value by 2. This was added as it was found that as the integration step was reduced, the spiders walked twice as fast. This was because the value was being added to the animation offset not per frame as originally planned, but per half frame or quarter frame etc. So as the time slices got smaller, the spider got relatively faster.
pCont.particleMatrix = maty
This stores the now incremented animation offset value back in the Particle Matrix Channel
$SPD_Master.Custom_Attributes.Ease_Curve_Value = maty.row1.x
The spiders custom attribute Ease_Curve value is now set to the stored animation offset value in maty.row1.x. This sets up the spider mesh to the correct shape for this particle on this frame.
new_mesh = Editable_Mesh()
A new empty mesh called Object01 is created to hold this mesh shape
$Object01.mesh = $SPD_MSH_Skin.mesh
The mesh shape is copied from SPD_MSH_Skin to Object01
$Object01.transform = pCont.particleTM
Object01 is moved and rotated to the same position/orientation of the particle
addModifier $Object01 (XForm ()) ui:off
Due to the original spider orientation the mesh appears backwards, so an Xform modifer is applied to correct this
$Object01.modifiers[#XForm].gizmo.rotation += quat 0 0 1 0
Once applied the Xform gizmo is rotated through 180 degrees on its Z axis to fix the orientation problem
pCont.particleShape = $Object01.mesh
Now that Object01 is correctly oriented its mesh is copied to the particle
delete $Object01
To finish off Object01 is deleted to clean things up
)
)
on Release pCont do
Again, this area of the script can be used for cleanup, in this case it is not used.
(
)
Chris Thomas
www.cg-academy.net