I realize this is a little late, but I thought I could help. For those without Box #1 or Box#3, I wrote a script operator that can align the particles to an animated surface and keep them on the surface while moving around and avoiding eachother. I set up a simple PFlow with:
Birth: 300 particles at frame 0
Position Object: Placed them all on the surface of my object
Shape Instance: I used teapots for the particles
Script Operator: I explain this below
Keep Apart: I used high amount of force (5000), but with limit on the speed (3.0) and acceleration (200.0). Also, I set it to use particle size (100% core, 200% falloff 0% variation). This keeps them apart without influencing the motion too much
Force: I used a wind force with 0.0 speed, 1.0 turbulence and 0.1 for scale and frequency. 100% influence in the PFlow operator. This makes the particles swish around the surface.
Display: Geometry
That’s all it needs. The script operator requires two objects: The first is the surface that you will be placing the particles on. This must be named theSurface and it must be an Editable Mesh. Otherwise the code will break. Also, there needs to be a second object (of any type) named theLookAtObject, which will keep the particles from spinning wildly around the surface normals of theSurface. Simply copy the code below into a Script Operator and it should work.
on ChannelsUsed pCont do
(
pCont.usePosition = true
pCont.useSpeed = true
pCont.useTM = true
)
on Init pCont do
(
)
on Proceed pCont do
(
theSurface = $theSurface
theLooker = $theLookAtObject
count = pCont.NumParticles()
IPM = MeshProjIntersect()
IPM.SetNode theSurface
IPM.Build()
IPM.IntersectRay [0,0,0] [0,0,1]
for i in 1 to count do
(
pCont.particleIndex = i
theCurrentSpeed = pCont.particleSpeed
theCurrentPosition = pCont.particlePosition
theClosestFaceChk = IPM.ClosestFace theCurrentPosition
theClosestFaceIndex = IPM.getHitFace() + 1
theClosestPoint = IPM.GetHitPos()
theClosestBary = IPM.GetHitBary()
theVertNormals = meshOp.getFaceRNormals theSurface theClosestFaceIndex
theCurrentNormal = normalize ((theVertNormals[1]*theClosestBary.x + theVertNormals[2]*theClosestBary.y + theVertNormals[3]*theClosestBary.z))
LookAtDirection = normalize (theSurface.pivot - theLooker.pivot)
q = dot theCurrentNormal LookAtDirection
LookAtDirection = normalize (LookAtDirection - q*theCurrentNormal)
sideDir = cross theCurrentNormal LookAtDirection
theCurrentTM = matrix3 -sideDir LookAtDirection theCurrentNormal theClosestPoint
pCont.particleTM = theCurrentTM
theCurrentSpeed += pCont.randDivergeVector theCurrentSpeed 15
pCont.particlePosition = theClosestPoint + theCurrentSpeed
)
)
on Release pCont do
(
)