View Full Version : Color by speed?

 Glacierise03-03-2007, 07:53 AMHi guys, I want to shade my particles according to speed, what should I do? Suppose this is a nice first step in scripting :D Maybe get the speed, normalize, use it as a UV sweep through a gradient ramp?
plejboy
03-03-2007, 12:26 PM
Hey.
Im hoping someone has a better solution but in the meantime here is a limited one:

So Im using a particleAge mat (Black,Grey,White)
I then use a Script to calc the velocity of the particles.
I then feed the Velocity into the ParticleAge. !!

For the Mat to work there needs to be a delete in the flow.
Just check how fast (approx) the fastest particle is moving and set the delete (by age) to that.

Bit of the script (I learnt this by Chris Tomas (http://www.christopher-thomas.net/pages/free_tutorials/tut_particle_flow_spiders/ct_tut_particle_flow_spiders.htm))

for i in 1 to count do
(
pCont.particleIndex = i
colmult = 80
pspeed = pCont.particlespeed
pvel = sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)
pCont.particleAge = pvel*colmult
)

Anyways. thats one solution. Allthough its very limiting since you usually want the pAge to accually be the Age of the particle :) I have been experementing alot lately with the particleAge and you can make some freaky flows if you use it for anything but AGE.

Bobo
03-03-2007, 03:57 PM
for i in 1 to count do
(
pCont.particleIndex = i
colmult = 80
pspeed = pCont.particlespeed
pvel = sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)
pCont.particleAge = pvel*colmult
)

Short note:

pvel = sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)

is equivalent to saying

pvel = length pspeed

but is 4 times slower. When calculating PFlow, you want the FASTEST method, because the code is called thousands and even millions of times.

When I run the first code one million times, I get 3360 ms (3.3 seconds).

st = timestamp()
pspeed = [10,2,3]
for i = 1 to 1000000 do
sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)
format "%\n" (timestamp()-st)

but

st = timestamp()
pspeed = [10,2,3]
for i = 1 to 1000000 do
length pspeed
format "%\n" (timestamp()-st)

evaluates in just 813 ms on the same machine, which is 4.13 times faster.

On top of that, assigning to a temporary variable pvel adds 200 ms to 1M calls in both cases, so saying

for i in 1 to count do
(
pCont.particleIndex = i
pCont.particleAge = length (pCont.particlespeed) * 80
)

without any intermediate variables could shave off up to 600 ms per 1M calls on top of the speed up from the length call.

Here is another test to simulate this - it contains 3 variable assignments within the loop and the original velocity calculation:

st = timestamp()
for i = 1 to 1000000 do
(
pspeed = [10,2,3]
colmult = 80
pvel = sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)
pvel * colmult
)
format "%\n" (timestamp()-st)

The above code is very similar to what the original code was doing (minus actually reading and writing particle values). It evaluates in 5750 ms.

st = timestamp()
for i = 1 to 1000000 do
(
pspeed = [10,2,3]
colmult = 80
pvel = length pspeed
pvel * colmult
)
format "%\n" (timestamp()-st)

The above executes in 3200 ms just because we got rid of the 3 additions and 3 multiplications within the MAXScript code - length() does the same internally, but is written in C++ and is thus much faster.

The inline version without any intermediate variables which calculates the same value at the end

st = timestamp()
for i = 1 to 1000000 do
(
length [10,2,3] * 80
)
format "%\n" (timestamp()-st)

needs only 1400 ms!

OlegB
03-03-2007, 05:59 PM
Quite an interesting study, Bobo. Thanks for raising the issue.

Being curious and all, I compared the calculation time for a Script Operator vs a Data Operator (from Box#3). I used a separate Script Operator to make time stamp. The operator sandwich follows:

Script Op: Time Stamp Get
Script Op: Modify age by speed
Script Op: Time Stamp Write

Here's the script of the Time Stamp Get:

on ChannelsUsed pCont do ()
on Init pCont do
(
global prevT = 0
)
on Proceed pCont do
(
prevT = timestamp()
)
on Release pCont do ()

Here's the script of the Time Stamp Write:

on ChannelsUsed pCont do ()
on Init pCont do ()

on Proceed pCont do
(
format "%\n" (timestamp() - prevT)
)

on Release pCont do ()

Here's the content of the Modify age by speed Script Operator:

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

on Init pCont do ()

on Proceed pCont do
(
count = pCont.NumParticles()
for i in 1 to count do
(
pCont.particleIndex = i
pCont.particleAge = length (pCont.particlespeed) * 80
)
)

on Release pCont do ()

I run the script for a PFlow system with 1,000,000 particles, and took timestamp measurement from frame 0 to 1, and got 3000ms. I got suspicious because the time is too round. Is it precision limits of the timestamp function? Just in case, I did the experiment with different particle count. For 2,000,000 I got 3031. Hmm, something is wrong. And THEN I recalled that I did not adjust the particle amount limit in PF Source. It means that all the time I was dealing with 100,000.

Ok, I adjusted the amount limit to 10 millions, and did the experiment for 500,000; 1,000,000 and 2,000,000:

500,000 - 14938 ms
1,000,000 - 30234 ms
2,000,000 - 61109 ms

Well, now I believe that the timestamping technique gives some reasonable data. At the same time I see that even the most simple scripts, when run for large amount of particles, are painfully slow. I may die from old age before my animation with large amount of particles and scripting will be ready. 30 seconds per frame for 1 million particles with the simplest scripting - oh, come on! Hmm, scripting is slow, and it can be used for relatively small amount of particles. But what about large counts? Here's Box#3 come to resque ( plug mode - On :) )

I made a Data Operator that does the same functions as the Script Operator above:

http://www.orbaz.com/forumData/images/20070303/DataOpSpeedMultAge.jpg

Now I was able to substitute the Script Operator with the Data Operator to make the timestamp measurements:

500,000 - 62 ms
1,000,000 - 125 ms
2,000,000 - 281 ms

Much better! Basically, for this script functionality the Data Operator is more than 200 times faster than the Script Operator. The speed gain is due to the fact that Data Operators use pre-compiled C++ functions, instead of the interpreted on-the-fly scripts - keep that in mind, Bobo - you have Box#3 at Frantic :)

Thanks,
Oleg B.

Glacierise
03-03-2007, 08:55 PM
Wow, Oleg, you really put a nail in PFlow scripting's coffin. Very, very good arguments you have there, will brush up my box 3 DV :)

Thanks for enlightening us on the issue Bobo, as speed is quite critical in PF :)

Bobo
03-03-2007, 09:39 PM
Much better! Basically, for this script functionality the Data Operator is more than 200 times faster than the Script Operator. The speed gain is due to the fact that Data Operators use pre-compiled C++ functions, instead of the interpreted on-the-fly scripts - keep that in mind, Bobo - you have Box#3 at Frantic :)

Preaching to the choir, don't we? ;)
I have always said - if you have Box #3, use that. If you don't, write a script and go drink coffee... Still, scripts are a valid proof of concept method for small amounts of particles.

Plus, everyone has scripting access in PFlow, not every Max user has the Boxes... So scripting is the least common denominator. Slow or not, I want to thank you very much for the time and effort you spent implementing scripting support in PFlow. Without it, many things would be practically impossible on tight production schedule, esp. in the early Box-less days!

Cheers!

depleteD
03-04-2007, 09:09 PM
Wow, this is the most hardcore thread I have ever read. Right on. I'm tottaly down with performance optimization. This was right down my alley. images/icons/icon10.gif

OlegB
03-04-2007, 09:30 PM
Thanks for the kind words, Bobo.

BTW, the Data Operators from Box#3 have performance profiling tools built right-in. Here's the operator's interface for profiling:
Interface: profileTools
Properties:
Methods:
<float>getPreCalcTime <time>time
<float>getNormalCalcTime <time>time
<float>getPostCalcTime <time>time
<float>getCalcTime <time>time
<float>getCalcTimeForInterval <time>timeStart <time>timeEnd

If you create a Data Operator, and you want to make it as effective as possible, first, you turn On profiling for an operator in the maxscript listener. Something like that:
\$'Data Operator 01'.useProfileTools = true

Then you can make a run for the animation in the viewport. After that you can use any of the methods to know the actual time spent by the data operator for calculations. Say, I would like to know how much time a data operator spends on calculating data from frame 5 to frame 30:
timeSpent = \$'Data Operator 01'.getCalcTimeForInterval 5f 30f

Then you can adjust the data flow wiring, optimize usage of the sub-operators, and compare the time for new data flow. I think this functionality was available since build 1.03 but it is not well known.

After optimization is finished, don't forget to turn Off the profiling:
\$'Data Operator 01'.useProfileTools = false

Thanks,
Oleg B.

PsychoSilence
03-05-2007, 09:35 AM
most scientific flow thread since i canīt remember when!

everything seems like i should get my hands on the box#3 :)

cheers

anselm

thatoneguy
03-05-2007, 07:55 PM
Now if only box 1 and 3 were part of the Max 9.5 subscriber update. :(

If I keep needing pflow as much as I am now, I'll just have to break down and buy it.

I've always wanted a maxscript equivalent to Box 3. A node based scripting language just seems like such a good idea. Especially in a field full of such visually oriented people.

CGTalk Moderation
03-05-2007, 07:55 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.

1