0% found this document useful (0 votes)
144 views0 pages

Magazine: Basics of Python Scripting With Realflow First Steps Creating Guis The Foam Project

real Fluid dynamics

Uploaded by

sreezb
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
144 views0 pages

Magazine: Basics of Python Scripting With Realflow First Steps Creating Guis The Foam Project

real Fluid dynamics

Uploaded by

sreezb
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 0

RF_magazine

REALFLOW USER MAGAZINE


FLUID DYNAMICS :: PHYSICAL SIMULATION :: SCRIPTING :: CONNECTIVITY :: REALWAVE
ISSUE 02_2007
RF_magazine is brought to you by Liquidlight.tv www.liquidlight.tv
Scripting with RealFlow - Introduction
Basics of Python Scripting with RealFlow
Learn how to use Python inside RealFlow
First Steps
Accessing particles and changing parameters
Creating GUIs
Write out your own user interfaces
The Foam Project
Learn, how to create foam and spray
and many more ...
RF_magazine
1
Welcome to RF_magazine 02/2007
This issue deals with scripting. With RF4, Next Limit
introduced a Python interface. Remembering the first
reactions in the old official forum, scripting wasnt very
popular at that time. But in the meantime, scripting
became one of the most important parts of the package.
On the other hand, many users are still afraid of using this
mighty tool.
When people think about scripting and programming,
many of them still have the impression that you need a
deep understanding of maths. This edition wants to show
- or even proof - that you can achieve stunning effects with
little effort. Python scripting in RealFlow is your friend, not
an enemy, because it makes life so much easier in many
ways.
The principles are fairly the same with each script.
RF_magazine uncovers these techniques with lots of
examples and projects. With this issue you can discover
the advantages and power of scripting.
Next Limit chose Python, because its an easy to learn and
wide spread language. In many terms, Python almost
reads as spoken English. This feature makes Python suited
for beginners. With growing knowledge and experience,
youll get a deep insight into Python and a better under-
standing of RealFlow. Altering parameters with a self-
written program is completely different from changing
values within in a Node Params window.
But theres one thing, even the best book or tutorial cant
do for you: Reading carefully and trying to understand
whats going on in a particular script. The basics, like Data
Types or simple Vector maths are very important and cant
be left out. Programming is also learning by doing!
So, dont hesitate and dive into scripting with RealFlow.
After a few hours youll be able to write your own first
script.
Sincerely yours,
Thomas
Thomas Schlick (tsn)
Liquidlight.tv
Nuremberg/Germany
www.liquidlight.tv
This magazine has been certified by Next Limit:
Editorial 02_2007
RF_magazine
2
Editorial
Welcome to RF_magazine Page 1
The Power of Scripting
A First Glance Page 4
What is Scripting Page 4
The Advantage of Scripting Page 4
Python and RealFlow Page 5
Where to Start? Page 5
The Basics of Python Scripting in RealFlow
Variables - First Steps with Scalars Page 6
Other Types of Variables Page 7
Creating Lists Page 8
Creating Dictionaries Page 8
Appending Values Page 9
Operators Page 9
Data Types Page 11
Vectors Page 13
Calculating with Vectors Page 13
Notation and Syntax Page 16
Getting Started
Setting the Preferences Page 18
The Scripting Windows Page 19
Scripted Objects Page 20
Your First Steps
Calling Emitters and Objects Page 22
Accessing Particles Page 23
Building Vectors Page 25
If & Else Page 27
Changing Attributes with Get & Set
Introduction Page 29
How to Use Get & Set Page 29
Working with Particles Page 34
Conclusion Page 36
Contents 02_2007
RF_magazine
3
Creating Graphical User Interfaces
Whats a Graphical User Interface Page 37
The Structure of RealFlow GUIs Page 37
File and Node Pickers Page 49
The Foam Project
Project Overview Page 41
The Nature of Foam and Spray Page 41
Creating Foam Page 42
Controlling Values Through Objects Page 46
Collision Based Foam Creation Page 47
Optimizing the Script Page 49
Threshold Parameters Page 50
Credits Page 50
Fun with Freeze
Project Overview Page 52
Freezing Time and Particles Page 52
Circular Particle Freezing Page 53
Relaxing Particles with Freeze Page 55
Inverting the Effect Page 55
Summary Page 56
Examples and Ideas
Overview Page 57
Image Based Emitter Speed Page 57
Boxed Daemons Page 58
Exporting Position Data to AfterEffects Page 59
Appendix
Reserved and Forbidden Words Page 64
Notes
Issue 01_2008 Page 65
Legal Notes Page 65
Contents 02_2007
RF_magazine
4
Scripting with RealFlow 02_2007
A First Glance
Since version 4, scripting is an important part of RealFlow.
Many well known studios asked for this feature and Next
Limit met this demand. The implementation of a scripting
interface was one of the major leaps in the development
of RealFlow and took almost two years.
Unfortunately therere still many users around, who want
to explore the power of scripting, but often dont know
where to start. Of course, the manual is an obvious source
for information regarding scripting. Fusion CI Studio
co-founder Dr. Mark Stasiuk did a very good job, as he
explains lots of fundamental questions and ideas behind
scripting.
Anyhow I met many people in search of a compendium or
a comprehensive tutorial, covering the entire range from
beginning to intermediate or even advanced scripting. At
realflowforum.com I already published a series of free
tutorials for beginners, and I had positive feedback. This
feedback encouraged me to deal with scripting in this
issue.
What is Scripting?
Scripting is the process of developing programs for custom
tailored applications. In our case, inside of RealFlow. For
scripting, a programming language is needed. This
language provides a set of statements and functions the
programmer can use, to write his own scripts. The main
difference between scripting and, e.g. C or Java program-
ming is the way, the code is treated:
Scripts are normally interpreted. You all know this from
web browsers. JavaScript and PHP are very popular
examples. The code (this is the sequence of statements in
your program) is often directly visible to the user. At the
moment, a visitor calls a webpage, the code is sent to the
interpreter and directly processed.
Unlike scripts, programs like RealFlow are compiled. This
means, the code will be preprocessed and converted into a
completely different format. Compiling has some great
advantages and the most obvious are speed and visibility.
A compiled program is much faster than an interpreted
script, because the compiler optimizes the code for certain
hardware needs. Simultaneously the program is converted
and the content becomes invisible. This helps to protect
your development. Today, there are already some compil-
ers available for scripting languages.
The Advantage of Scripting
I was talking about the benefits of compiled programs.
Now, youll surely ask, why does RealFlow use a scripting
language and not C/C++ or Java? With scripting languages
you dont have to deal with compiling. This sometimes
can be a very heavy task to solve and you need certain
compilers for different operating systems and hardware
requirements.
With the available scripting languages you dont have to
care about all these things. You just start coding, execute
the script and the interpreter does everything else. Many
people also say that its easier to start with a scripting
language, but in my personal opinion, its also a matter
of what you want to do. Both, interpreted and compiled
languages have clear benefits and disadvantages.
Modern scripting languages are very powerful tools with
hundreds of extensions and libraries, mighty built-in
functions and sophisticated methods, like object orienta-
tion.
Often used scripting languages are Perl, Python, Java-
Script, VBScript, PHP, Tcl and also AppleScript. During the
first years of the WWW, Perl was the main language for
online applications. After the rising of PHP and Python,
Perl lost its importance, but still has a vivid and strong
community.
The Power of Scripting
RF_magazine
5
Scripting with RealFlow 02_2007
Python and RealFlow
Now, Python is an integral part of RealFlow, but there is
much more: The Next Limit developers extended the
existing Python language by hundreds of new statements
and functions. You can now directly access almost any
object and attribute of RealFlow via Python scripting.
This fantastic development opens the door to an infinite
variety of possibilities.
RealFlow comes together with a complete version of
Python and the user doesnt have to struggle with instal-
lation directories and libraries. With the installation of
RealFlow you get the complete Python package, ready to
start.
Some of the main advantages of Python are:
Free license, even for commercial use
Common and wide spread language
Perfect for beginners
OS independent (Unix, Linux, OS X, Windows etc.)
Hundreds of online resources, books and forums are
available
Standard scripting language for many applications
Code is platform independent and easy to exchange
Python scripting in RealFlow lets you directly address
object, daemon and emitter parameters and alter them.
Where to Start?
One of the most important questions for beginners is
where to start. Many users are doubtful and insecure
when they think about making their own scripts.
One very common prejudice is that scripts always have to
be complicated and highly sophisticated artworks.
Just discard this attitude and think of it as a normal
language. Not every sentence has to be a poem, mostly
its better to speak in a clear and easy way.
Another misunderstanding is that you have to be a maths
genius to write good programs. Of course a basic knowl-
edge of mathematics is useful, but not mandatory. If you
just want to automate some processes, you wont need
very much mathematics.
A very good starting point for each programming novice is
to create a simple example with a great feeling of success.
Since RealFlow is a 3D application with a high visual
impact, its rather easy to find those examples. No matter
what youre doing, it will directly influence the objects on
your screen.
Maybe youve already started to learn a programming
language? Then you also might have seen the famous
Hello World program. This is always a nice start, but for
our purposes its not suitable. Were after completely
different things.
Before we can kick off, its indispensable to learn the
basics of Syntax and Notation to understand the principles
of how Python works.
The Power of Scripting
Fig 1. Script affected particles of an emitter
RF_magazine
6
Scripting with RealFlow 02_2007
Variables - First Steps with Scalars
Variables are the key to everything, independent of the
used programming language. A Variable could be consid-
ered as a placeholder. You can fill this placeholder with any
content you like. In programming languages, Variables
have to be declared. This means that you have to introduce
a name for your Variable and assign a value to it.
Imagine the following easy example. We got some infor-
mation from a person: 25, female, Claudia. Without setting
these information into a logical context, we cant capital-
ise on these data. Im sure you already started bringing
these information into an order:
Name = "Claudia"
Age = 25
Gender = "female"
Now these attributes start making sense. You assigned
certain properties to Claudia and you already declared
three Variables. The names of these Variables are Name,
Age and Gender, their properties (or values) are Claudia,
25 and female.
You can also see, that its very important to choose strong
and meaningful names for your Variables. In this case we
declare our Variables like this:
A = "Claudia"
B = 25
C = "female"
Formally thats absolutely correct, but the information
isnt very clear, especially when there are similar values or
properties:
A = "Claudia"
B = 25
C = "Beatrice"
D = 56
After a while, youd certainly have no clue what the
Variable names are standing for. With Variables declared
like this, its no problem:
First_Name = "Claudia"
Age = 25
Second_Name = "Beatrice"
Weight = 56
Its very important that Variables always share the same
name over the entire script, because Python and other
languages are case sensitive. In a script Gender is not the
same as gender. Variables can be declared wherever
theyre needed, but its a good idea to find common
places for your Variables and arrange them clearly. Never
use special characters for Variables names. The range of
allowed characters is a-z, A-Z, 0-9 and the underscore.
Variables must start with a character, not with a number.
Variables are a very flexible facility. You can change their
values within the script and you can assign numbers,
characters, and strings. But each of the Variables above
can only carry one value. This type is called a Scalar.
By defining a new value for an existing Variable, the old
value will be substituted:
Name = "Claudia"
Name = "Lydia"
If we had a script to print out the variables content, the
result would be Lydia. Only the very last value will be used.
An important convention is that strings and characters
have to be written in quotation marks. With numbers this
is not necessary. If youre writing numbers within quota-
tion marks, theyll be treated as a string.

The Basics of Python Scripting in RealFlow
RF_magazine
7
Scripting with RealFlow 02_2007
Other Types of Variables
As youve learned, a Scalar can only carry one value, but
there also are Variables, you can fill with two or more
values. Python programmers differentiate between two
types: Lists and Dictionaries. A List is something, we
certainly need very often with Python and RealFlow.
You can consider a List as a cupboard with drawers. Each
value has its own drawer, and theyre all arranged in a
fixed order. The numeration of these drawers starts with 0
(Zero). Python also starts the numeration of Lists with 0.
Lets assume that each drawer contains coloured marbles:
0 = red, 1 = blue, 2 = green, 3 = yellow, 4 = purple
By opening drawer number 2, youll have access to the
green marbles, opening number 0, will lead to red ones.
The List provides a container with a fixed sequence.
Whenever you want to have purple marbles, you have to
call position number five. But wouldnt it be nice to have
a method to arrange the marbles the way we want?
The solution is called Dictionary. In a Dictionary, theres
no sequence and all contents could be disordered.
In difference to Lists, its not necessary to call the entries
via a fixed index. In Dictionaries we use search keys.
A very good example for a Dictionary is a private phone or
address book, in which we often see pairs of values, e.g.
a name and a phone number. These pairs are stuck
together and we cant identify one without the other:
Claudia : 54358
Lydia : 43663
Agnes : 65586
Susan : 94343
Imagine the phone book from your cell phone. By entering
a name, the software prints out the according phone
number. Thats exactly the way, a Dictionary is working.
It doesnt matter, where the entries are located within the
book, because theyll always be identified via a search key.
In RealFlow Dictionaries is rarely used, but nevertheless
there might be some cases, where you need them.
Now you know all three types of Variables for storing
values in Python and respectively in RealFlow: Scalar,
List and Dictionary. For Lists and Dictionaries you have to
obey the same rules as for Scalars. The Variables names
should be meaningful and special characters are forbid-
den.
Just one final note: a List is sometimes called an Array, and
a Dictionary is also known as Hash or Associative Array.

Fig 2. A List can be used to arrange values and entries
The Basics of Python Scripting in RealFlow
RF_magazine
8
Scripting with RealFlow 02_2007
Creating Lists
Creating a List structure in Python is an easy task. An
empty List is written as:
my_list = []
If you want to define the values directly with the creation
of the List, the format is:
my_list = [value1,value2,value3, ...,value n]
For better understanding, Id like to create a List with the
colours of our marbles from page 7:
colours = ["red","blue","green","yellow","purple"]
As you can see, all colour names are between quotation
marks. You dont need these marks for numbers:
diameter = [2.0, 2.5, 3.0, 3.5, 4.0, 4.5]
In Python its easy to find elements, stored in a List:
favourite_colour = "yellow"
colours = ["red","blue","green","yellow","purple"]
if (favorite_colour in colours):
print "Your favourite colour is in stock."
else:
print "Sorry. Your favourite colour is not available."
Result: Your favourite colour is in stock.
In this short example, I introduced a Scalar Variable,
named favourite_colour. The value is yellow. Now, the in
statement goes through the List in search of yellow.
If yellow has been found, the script writes out a success
message. If the value of favourite_colour (e.g. brown) isnt
part of colours, the result would be
Sorry. Your favourite colour is not available.
The next method uses the stored indices. Since each value
has a fixed position within the List, its possible to address
a colour using this index. Remember that List indices
always start with 0.
colours = ["red","blue","green","yellow","purple"]
selection = colours[2]
print selection
Result: green
Another very important function with Lists is to find the
total number of elements. This is also called the Length of
a List. You can get it with:
colours = ["red","blue","green","yellow","purple"]
number_of_entries = len(colours)
print number_of_entries
Result: 5
Creating Dictionaries
Youve already learned that elements in a Dictionary dont
need a specific order to find them. A Dictionary uses
key/value pairs to search through the content. An empty
Dictionary can be created by typing
my_dictionary = {}
Of course you can directly start entering values and
performing a query:
phonebook = { "Claudia":54358, "Lydia":43663,
"Agnes":65586, "Susan":94343}
print phonebook["Agnes"]
Result: 65586
Isnt that easy? You dont have to worry about indices,
numeration or positions.
The Basics of Python Scripting in RealFlow
RF_magazine
9
Scripting with RealFlow 02_2007
Analogue to Lists, its also possible to get the length of a
Dictionary. The function calls all key-value pairs and stores
the result in a predefined Scalar:
phonebook = { "Claudia":54358, "Lydia":43663,
"Agnes":65586, "Susan":94343}
number_of_entries = len(phonebook)
print number_of_entries
Result: 4
Appending Values
Now you know how to create Lists and Dictionaries manu-
ally and how to read out specific values. In RealFlow we
often have to deal with thousands of particles. Collecting
their data and writing them to a List by hand wont be a
good solution. For this purpose, Python provides a special
function called append.
The append function adds values to the end of a List. This
is important, since a List is working with index numbers.
There are also functions to insert values at certain
positions, but theyre rarely used with RealFlow. In most
cases you extend a List by simply appending a new value.
This is the syntax for Lists:
colours = ["red","blue","green","yellow","purple"]
new_colour = "orange"
colours.append(new_colour)
Adding a value to Dictionary is slightly different. We dont
need a fixed order and therefore the position of the new
element isnt important. An entry of a Dictionary always
consists of two elements: The key and its value. Its very
important to define which one will serve as a key, respec-
tively as a value.

phonebook = { "Claudia":54358, "Lydia":43663,
"Agnes":65586, "Susan":94343}
phonebook["Helen"] = 22986
Its also possible to use Scalars and add their values to the
Dictionary:
new_name = "Helen"
new_number = 22986
phonebook[new_name] = new_number
For the moment this should be enough. Youll learn more
about these data types in connection with scripts.
Operators
Operators are one of the most important things in Python.
With an Operator its possible to perform basic arithmetic
calculations and comparisons. And thats truly a bless.
Without Operators, you wouldnt be able to compare
particle velocities or positions against a Scalar and thered
been no way to multiply or substract values. You can find
Operators anywhere in a script and Python knows a lot of
them. The first group contains all arithmetical Operators:
+ Addition 15 + 17 = 32
- Substraction 64 - 30 = 34
* Multiplication 10 * 12 = 120
/ Division 80 / 10 = 8
** Exponentiation 12 ** 2 = 144
% Modulus 28 % 7 = 0
+ String concatenation John + Doe = JohnDoe
* String repetition Hi * 3 = HiHiHi
A very nice Operator is Modulus, but it needs some expla-
nation. In general, Modulus tells you, if theres a remain-
der after performing a calculation or not. With Modulus
its possible to find out whether a number is even or not,
for example. If the result is different from 0, the number is
uneven. A result of 0 indicates an even number.
The String Operators are rarely used with RealFlow and
more important for text processing applications.
The Basics of Python Scripting in RealFlow
RF_magazine
10
Scripting with RealFlow 02_2007
The next group contains Operators for comparisons:
< Less than
> Greater than
<= Less than or equal
>= Greater than or equal
== Equal
!= Not equal
Equality is tested with ==. The single = is just used for
assignment, e.g. in Variable names:
Name = Claudia
The result you get with Comparison Operators is true or
false. These are the two possibilities and theres nothing
between:
velocity_a = 5.0
velocity_b = 7.3
if (velocity_a < velocity_b):
print "This is true."
By swapping the Variables, the result would be false. In
RealFlow you normally wont calculate with true or false.
Its just a means for making decisions, what should
happen with your values or parameters. It can be consid-
ered as a crossover, where you also have two or more ways
to go. Lets say, you want to limit the velocity of a parti-
cle:
max_velocity = 7.5
current_velocity = 8.2
if (current_velocity > max_velocity):
new_velocity = max_velocity
else:
new_velocity = current_velocity
What happens here? The current velocity of a certain
particle has been determined as 8.2. With an Operator
were testing, if the value of the current velocity is greater
than the maximum velocity. This value is a threshold.
Whenever a particles velocity is greater than this limit,
its just cut down to 7.5. If this condition is not fulfilled,
the script calculates with the current value, stored in
current_velocity. In this example, the condition is true,
because current_velocity is actually greater than
max_velocity.
Another example using the equality Operator:
max_y_position = 3.5
current_y_position = 2.9
if (current_y_position == max_y_position):
attractor_force = 25
else:
attractor_force = 10
At the moment, the observed particle reaches the
max_y_position value, attractor force will be 25. Here,
current_y_position is less than 3.5, and the condition is
false. The assigned value for attractor_force is 10.
A third group consists of so called Boolean Operators.
The keywords are:
and
or
not
Youll mostly need these Operators when you have to
check more than one condition.
if (cur_vel >= threshold_1 or cur_vel <= threshold_2):
gravity_force = 9.81
else:
gravity_force = 0
In this case, were checking, whether the current velocity
lies between two threshold values. If the condition is true,
the gravity force will be activated.
The Basics of Python Scripting in RealFlow
RF_magazine
11
Scripting with RealFlow 02_2007
The last class Im talking about here, are Augmented
Operators. This is a very special class and mostly used for
abbreviations. In some cases, its necessary to write
expressions like:
while (particles):
total_mass = total_mass + particle_mass
In this example, the script loops through all particles and
sums up their individual masses to get a total mass value.
This calculation could also be written as:
while (particles):
total_mass += particle_mass
The most common Operators are
+= -= /= *= **= %=
There are some more Operators in Python, but theyre not
often used with RealFlow. By knowing the discussed
classes, you already have the tools to create your own
conditional expressions and basic calculations.
Data Types
Coders have to distinguish between various Data Types.
At school you probably already met most of them and
with RealFlow you will need these types again. The most
common format is surely the Integer type. Integers are
wide spread and we use them in our daily life without
thinking about them. Integers are numbers like:
-5, 2, 100, -4335, 757, 423843, -3289988, 45, 234, -775647
The set of Integers is infinite, it can be positive or negative
and it doesnt contain fractures like 3.645, -1.3333 or 12.7.
Fractured elements arent members of the Integer family.
Also fractures, apparently consisting of two Integers arent
valid elements: or . Though the result of such a
division can be an Integer!
There are two subtypes of Integers in Python. The first one
is called Plain Integer, or just Integer. The abbreviation of
this type is known as int. This type is not endless and
theres a maximum range. For the sake of calculation
performance its necessary to differentiate, because Plain
Integers are processed much faster. In 99% of all cases, the
Plain Integer type will serve your needs. These are the
specifications:
int (Integer or Plain Integer)
2,147,483,648 to +2,147,483,648
This range is also called 32-bit precision and its the
minimum for all modern operating systems. Most operat-
ing systems also support 64-bit precision. The valid range
for this type is:
9,223,372,036,854,77,.808 to +9,223,372,036,854,775,808
long (Long Integer)
In Python this subtype has infinite precision, but the
effective length of your number depends on the amount
of RAM in your computer.
The next, very important type is called Float. Youve
already seen Floats (also called floating point numbers)
on the left. They were introduced as decimal fractures
(1.8543). The most significant attribute of Floats is the
decimal point. Its possible to calculate with decimal
places of different length. Of course its allowed to
combine Integers and Floats together. Valid expressions
would be:
75.543 / 8.4 + 1.3 * 67.55
5.8564 * 6 + 23.98
Floats can also be negative or positive. The length or
precision of Floats strongly depends on your computer and
operating system, but most likely you wont encounter
any problems. The token in Python is simply float.
The Basics of Python Scripting in RealFlow
RF_magazine
12
Scripting with RealFlow 02_2007
The last Data Type, Im talking about, is called Complex. In
RealFlow this type will be rarely used and its only men-
tioned for completeness. Complex numbers consist of two
parts: A real and an imaginary part. The Complex type is
therefore always written as a pair of numbers. The real
number is just an Integer or a Float type, while the imagi-
nary part must own a j character:
(3 + 6j)
Even if the imaginary part is Zero, you have to write this
value in conjunction with j:
(12 + 0j)
With Complex numbers its possible to perform calcula-
tions as usual:
(2 + 4j) * (8 + 3j) or (11 + 1j) * (7 + 5j)
In Python, this special Data Type can be introduced with a
complex statement.
Now youve heard about int, long, float and complex,
but you surely have no idea, how to use these types. One
main application for Data Types are Variables, of course.
The good thing is that you dont have to introduce each
Variable with the appropriate index. Python automatically
recognizes the correct type. But there are some other
cases, where you have to determine, which type youd like
to use. One of these cases are GUIs - Graphical User
Interfaces. Youll read about GUIs later, starting on
p. 37, but Id like to introduce some basic information
about the usage of Data Types:
With GUIs its often allowed and wanted, to enter custom
values for initializing a calculation. Lets assume, your goal
is to build a tower made of cubes. Within the GUI you can
type in values for the number of stacks, the width and the
height of a stack. This number has to be an Integer,
because you cant build a tower with 5.278 floors. The
width and height instead, can measure 2.5 or 7.84 units.
The used Data Types in this example are:
levels = int e.g. 10
width = float e.g. 2.5
height = float e.g. 3.75
Another often used exercise is the conversion of Data
Types. Its possible to translate a Float number into an
Integer and vice versa. You could also translate a Long type
into a Float number. But with all these transformations,
you have to keep in mind that youre probably losing
precision. Converting a Float type number into an Integer
truncates the decimal places:
int(3.256) = 3
int(9.999) = 9
This conversion only takes the Integer part of the original
number, without caring about rounding. The same can be
observed when converting Long to Float.
The Basics of Python Scripting in RealFlow
Fig 3. A tower based upon integer and float values
RF_magazine
13
Scripting with RealFlow 02_2007
Vectors
Another Data Type are Vectors. Vectors are not a built-in
element of Python, but there are some languages,
supporting Vectors as a Data Type. The Python extension
of RealFlow, for example, knows Vectors and theyre fully
integrated. Since theyre not part of Pythons standard
installation, Id call them a Pseudo Data Type here.
In RealFlow, Vectors are one of the most important
elements and this is another reason, why I chose to
discuss them separately.
A Vector could be considered as an arrow, pointing into a
certain direction. To describe a Vectors direction, we need
at least two dimensions: x and y. By drawing a Vector into
a coordinate system, we get the main properties:
Direction and Magnitude. The Direction tells you, where
the Vector is pointing at, while the Magnitude is the length
of the arrow.
Vector coordinates can consist of Integers and Floats.
The graphical illustration of a Vector might help to get
an understanding, but its not suitable for calculations.
Therefore we have to find a notation. Therere some
established forms to represent a Vector, but according to
RealFlows notation, a Vector in RF_magazine is always
written as:
a= (x, y) or a = (1.0, 1.0)
In RealFlow were dealing with 3D space and the represen-
tation of a Vector needs a third coordinate:
a = (x, y, z) or a = (1.0, 1.0, 1.0)
If yourre interested in real life examples or the math-
ematical background, Id suggest to have a look at physics
books or the internet, e.g. Wikipedia.
Calculating with Vectors
Its not possible to calculate with Vectors directly. The
most common calculation type with Vectors is Addition.
For performing Vector calculations, you have to use either
the individual elements x, y, z or its Magnitude. The
coordinates of a Vector are called Scalars. You already
heard about Scalars at the very beginning of this chapter.
a = (5.0, 2.5, 4.2)
b = (1.7, 3.9, 8.1)
In this case were looking for c = a + b. Maybe its just
c = (a
x
+ b
x
, a
y
+ b
y
, a
z
+ b
z
)
c = (5.0 + 1.7, 2.5 + 3.9, 4.2 + 8.1)
c = (6.7, 6.4, 12.3)
This really is the answer, and its analogue with Vector
Substraction c = a - b:
c = (a
x
- b
x
, a
y
- b
y
, a
z
- b
z
)
c = (5.0 - 1.7, 2.5 - 3.9, 4.2 - 8.1)
c = (3.3, -1.4, -3.9)
As you can see here, a Vector can also point into negative
directions.
The coordinates of a Vector are Scalars. For this reason its
possible to multiply the Vector components with another
Scalar. Its likely that you will use this method very often,
especially with forces.
The Basics of Python Scripting in RealFlow
Fig 4. Different vectors pointing at various directions
y
x
RF_magazine
14
Scripting with RealFlow 02_2007
Multiplication with a Scalar is calculated this way:
a = (4.8, 2.9, 1.4)
s = 2.5
b = s * a
b = (s * a
x
, s * a
y
, s * a
z
)
b = (2.5 * 4.8, 2.5 * 2.9, 2.5 * 1.4)
b = (12, 7.25, 3.5)
Another very important calculation rule is the Scalar
Product. Dont mix it up with last rule, the Multiplication
with a Scalar. These operations are completely different!
The result of a Scalar Product is a Scalar and it can be
considered as the multiplication of two Vectors:
a = (1.7, 7.3, 3.3)
b = (2.4, 0.7, 5.1)
c = a * b
c = a
x
* b
x
+ a
y
* b
y
+ a
z
* b
z
c = 1.7 * 2.4 + 7.3 * 0.7 + 3.3 * 5.1
c = 4.08 + 5.11 + 18.83
c = 26.02
A special form of the Scalar Product is the square of a
Vector:
c = a
2
c = a
x
2
+ a
y
2
+a
z
2
Extracting the square root from this term, yields to a
Vectors Magnitude (often called Norm or Length).
| c | = a
x
2
+ a
y
2
+a
z
2
As you can see clearly here, the result of this operation is a
Scalar, not a Vector. The lines around c indicate that its a
Norm. In RealFlow you wont have to worry about this
rule, because theres a built-in function for calculating the
Scalar Product. In RealFlow its called Dot Product, repre-
sented by an asterisk character (
*
).
The next rule, Im talking about, is named Cross Product.
The result of a Cross Product is also a Vector. The most
common way to calculate a Cross Product is done by
multiplying the components of two Vectors:
a = (2.2, 6.1, 1.0)
b = (0.7, 0.4, 3.8)
c = a x b
c
x
= a
y
* b
z
- a
z
* b
y
c
y
= a
z
* b
x
- a
x
* b
z
c
z
= a
x
* b
y
- a
y
* b
x
c
x
= 6.1 * 3.8 - 1.0 * 0.4
c
y
= 1.0 * 0.7 - 2.2 * 3.8
c
z
= 2.2 * 0.4 - 6.1 * 0.7
c
x
= 23.18 - 0.40 = 22.78
c
y
= 0.70 - 8.36 = -7.66
c
z
= 0.88 - 4.27 = - 3.39
c = (c
x
, c
y
, c
z
)
c = (22.78, -7.66, -3.39)
This operation is also implemented in RealFlow by default
and you dont have to calculate this manually. Many
physical values can be determined with the Cross Product,
e.g. the Lorentz Force or the torisonal moment. There
surely comes a time, when you start looking for special
formulas to enhance your simulations. Then youll
certainly meet the Cross Product again. Many formulas,
dealing with the simulation of natural, turbulent phenom-
ena use the Cross Product to mimic the desired forces.
The last rule has already been introduced: The Norm or
Magnitude of a Vector. The result of this operation is a
Scalar. Youll often use Magnitudes with forces. The
resulting Scalar can be multiplied with a Vector again (see
Multiplication with a Scalar above). The Magnitude is
calculated using an old friend: Pythagoras theorem.
If you cant remember it, dont worry and turn over...
The Basics of Python Scripting in RealFlow
RF_magazine
15
Scripting with RealFlow 02_2007
Pythagoras theorem is normally used for triangle calcula-
tions. In Vector maths, where you also could represent a
Vector as an arrow, its possible to extract values by
constructing triangles from the given Vectors.
With Pythagoras theorem youre now able to get the
Length, or Magnitude of a Vector:
Pythagoras theorem for right-angled triangles:
a
2
+ b
2
= c
2
You can get the value for c by extracting the square root:
c = sqrt( a
2
+ b
2
)
This term is also valid in three dimensional coordinate
systems and for Vectors. The Magnitude of a Vector is
always positive, because you have to square the values.
a = (3.2, 2.8, 1.5)
| a |= sqr(a
x
2
+ a
y
2
+a
z
2
)
| a ] = sqr(10.24 + 7.84 + 2.25)
| a | = 4.51
For fluid, smoke and fire simulations, physicians intro-
duced Vector Fields. In a Vector Field, a Vector is assigned to
each point of the considered space. The idea is to create
field of variable strength at different points. Very good
examples are gravity or the magnetic field. In reality these
depend on where you are and therefore have different
values.
Creating Vector Fields in RealFlow with Python is not
trivial. For this reason I dont want to dive into the depth
of these fields.
The maths behind Vector Fields is complicated and the
main problem with RealFlow is that we need the previous
and the current value to solve the equations. RealFlow
doesnt provide functions to store values over time and
substitutes the previous results.
A big variety of very interesting Vector Fields can be
explored here with a Java Applet:
http://www.falstad.com/vector3d/
The Basics of Python Scripting in RealFlow
Fig 5. The Magnitude of a Vector
Fig 6. 3D representation of a Vector Field with arrows
x
y
a
b
c
pos
RF_magazine
16
Scripting with RealFlow 02_2007
Notation and Syntax
Now youre getting closer to scripting and the theoretical
part lies almost behind you. In this chapter youll learn
how to organize a RealFlow script and what you have to
attend.
When youre writing Python scripts for RealFlow you have
to follow certain rules. Without obeying these rules, youll
receive error messages and your program wont run. The
best idea is, to avoid those errors from the very beginning.
As Ive seen in the unofficial RealFlow forum, the most
common error is related to shifting and spacing. Python
recognizes clauses, conditions and syntax groups auto-
matically. The indicators are leading tabs and blanks.
program start:
statement 1
if ( condition ):
statement 2
else:
statement 3
while loop:
statement 4
if ( condition ):
statement 5
else:
statement 6
statement 7
The example above is a typical structure of a Python script.
For each new instruction set, theres got to be a new
clause. The interpreter allows blanks as well as tabs, but
you must never use them together. Always make a
decision on one method for shifting. Id recommend to use
the Tab key. With this key you dont have to worry about
the number of blanks and this makes life much easier.
Other scripting languages, like Perl, often use brackets to
separate statements and functions. The advantage is that
you dont have to care about tabs, but you shouldnt
forget to close all branches. Otherwise youll receive an
error. This an example of a code structure in Perl:
sub function {
statement 1;
if ( condition ) { statement 2 }
else { statement 3 }
while loop {
statement 4
if ( condition ) { statement 5 }
else { statement 6 }
statement 7
} # end while
} # end sub function
Though all conditions, definitions and loops are embedded
into branches, its still a very good idea to use tabs for
better readability. With Python its essential and especially
with copied and pasted scripts, you have to be extremely
careful. Scripts from forums, discussion boards or other
sources often dont follow these rules for leading tabs,
and this directly leads to problems.
Python is a very flexible programming language. It allows
many short forms and convoluted statements. For begin-
ners its often very hard to understand, whats going on in
scripts, made by Python pros. To grant access to everyone,
the scripts from this issue use a step-by-step technique.
Thus many scripts may be longer than necessary, but
beginners can follow the code, too. I leave it to you, to find
the shortest form for a script. With growing experience
youll surely learn how to contract statements effectively.
Another issue is the spelling of Variables. Python and the
RealFlow extension use certain words for introducing
particular functions. These words cant be used for
Variables. You can find a list of the currently reserved
words on page 64. Another limitation with Variable names
is the usage of the dot character. The dot character is used
as an identifier for an objects properties, like:
emitter.getPosition()
The Basics of Python Scripting in RealFlow
RF_magazine
17
Scripting with RealFlow 02_2007
The Syntax of a script can be seen as a grammar. Violating
the rules leads to errors. Therere many definitions and
essays about Syntax and especially for beginners its not
easy to understand its importance. In brief terms:
Syntax is the logical structure of a script.
This structure is not a fixed rule type, its flexible and
theres always more than one way to achieve a result.
I think this should be enough about the nature of Syntax.
If you need further information, just have a look at the
internet.
One of the most common error messages youll receive in
RealFlow is Script Syntax error at line XX. A Syntax error
often has marginal reasons, like:
leading spaces are wrong
wrong order of statements
using Variables before assigning them
misspelling of Variable names
unmatched brackets
infinite loops
using improper operators
using reserved or forbidden words
using the wrong Data Type
The Basics of Python Scripting in RealFlow
Fig 7: RealFlow error message
RF_magazine
18
Scripting with RealFlow 02_2007
Setting the Preferences
RealFlow provides a complete scripting environment, fully
equipped with Syntax highlighting, a debugger (this is an
error detection facility) and a set of predefined func-
tions. To adjust this environment to your needs, Id
recommend to set the scripting preferences first:
realflow > Preferences > Script
After the Script tab has been selected, a new panel
appears, showing a variety of settings:
Adjusting the Syntax Color is a matter of taste. You can
choose any colour you want. With the predefined colours,
a typical statement would look like this:
# Get the scene emitter
particle_source = scene.getEmitter("Circle01")
Highlighting certain keywords of a script helps you to keep
the code readable and its easier to differentiate the
individual elements.
The Tab size determines, how many leading blanks are
inserted to structure the Python code. You should always
use tabs instead of blanks or spaces to introduce new
instructional blocks (also see p. 16). In conjunction with
this setting, its a good idea to use a so called monospace
font. Glyphs from these fonts all have a the same fixed
width, which is great for organizing a script. With mono-
space fonts its possible to arrange Variable names like
this:
pi = 3.14
gravity = 9.81
e = 2.718
ini_vel = 12
With fonts using variable widths, its not possible to align
names, numbers and other characters correctly:
pi = 3.14
gravity = 9.81
e = 2.718
ini_vel = 12
Very common monospace fonts are Monaco, which is used
here for all scripting examples, and Courier. Both fonts
should be installed on most computers by default.
In RealFlow its possible to choose a directory for your own
scripts. These scripts can be filed in the toolbar of RealFlow
and you have direct access to them, just by clicking on the
appropriate icon. You can either use the default path or
define another location anywhere on your harddisc(s). The
Scripts Organizer file carries all information about the user
scripts.
The last three icons are customized and indicate some of
my own scripts. Please note that its only possible to use
Batch Scripts (see p. 19) with the toolbar. Many free scripts
already come with icons, ready to use with the toolbar.
After youve made the settings, just click OK to use them
as new defaults.
Getting Started
RF_magazine
19
Scripting with RealFlow 02_2007
The Scripting Windows
The Scripting Windows are a true obstacle for many
beginners, because they simply dont know which type to
use. In RealFlow there are three basic types: Batch, Events
and Custom. The Batch and Events Script windows can be
called via the menu bar or by pressing F10, respectively F11:
Layout > Batch Script
Layout > Events Script
Batch Scripts
Lets have a look at Batch Scripts first. Whats a Batch
script and whats it used for? In Batch Scripts you mostly
define routines for repeating tasks. Good examples are:
Creating a brick wall
Simulating two or more scenes successively
Changing hundreds of values simultaneously
Creating a basic scene with default objects
Changing states from inactive to active
Adding constraints to multiple objects
Have you ever built a brick wall in your 3D application? You
have to set brick by brick, or use a copy and paste method.
With scripting you can define rules, how many floors the
wall should have and how many bricks you want to use.
The arrangement will be done automatically while
running the Batch Scripts. After this task has been
completed, another Batch Scripts could alter the mass of
each brick to get a more random effect, while destroying
the wall with a bullet, for example.
Whenever you need to change properties, create lots of
similar objects, export values or add features, a Batch
Scripts is the proper choice. The way of developing Batch
Scripts is the same as coding Events or Custom Scripts. The
main difference lies in the way, the script will be executed
by RealFlow.
Events Scripts
The second type, Events Scripts is needed, whenever you
want to influence particles or objects directly during the
simulation process. With a Batch Script you dont need to
start a simulation, with an Events Script its crucial.
RealFlow knows a couple of predefined events:
onSimulationBegin
onSimulationEnd
onSimulationStep
onSimulationFrame
onChangeToFrame
onSimulationBegin is suited for applying initial param-
eters, You can also add some basic elements to a scene
that will be used for simulation. After this initialization,
the routine wont be called again. onSimulationEnd works
analogue to onSimulationBegin. Maybe the most impor-
tant event is onSimulationStep. Here, RealFlow applies
calculations for each time step. With heavy maths, this
may result in long simulation times. Please also remember
that scripts only use one processor/core of a computer.
Fig. 8: The Batch Script window
Getting Started
RF_magazine
20
Scripting with RealFlow 02_2007
Maybe the best example for a script using an onSimula-
tionStep event is the well known foam script. This script
will be discussed later as separate project, starting on
page 41.
With onSimulationFrame its possible to make changes on
certain frames. The actual frame serves as trigger to
switch on or off certain properties of one or more objects.
Also changes can be performed with each frame, e.g.
animating an object or exporting custom bin files.
The last type, onChangeToFrame is great for post-
processing. With this event, scripts can be applied while
playing back a cached scene. Its rarely used, but neverthe-
less a powerful means for some applications.
After opening the Events Scripts window, youll recognize
that these functions are already implemented.
Each function wears a pass statement. This statement tells
RealFlow to simply jump over this function. By removing
pass and replacing it through your own code, RealFlow
starts executing the script. The best thing is that youre
not limited to just one of the given functions. You can
initialize a scene with onSimulationBegin, activate objects
with onSimulationFrame and perform calculations using
onSimulationStep. Thats no problem, but before youll be
able to run a script, you have to check Active.
Scripted Objects
Custom scripts are sectioned into a couple of subtypes and
theyre surely the most powerful means to customize your
simulations. The three types are:
Scripted Daemon
Scripted Emitter
Scripted RealWave
The Scripted Daemon is probably the most often used
subtype. In simple terms, a Scripted Daemon applies a
force to an object. For this purpose, the scripting window
provides three predefined functions:
applyForceToEmitter( emitter ):
applyForceToBody( body ):
removeParticles( emitter ):
Its necessary to distinguish between particles and (rigid)
bodies. With a Scripted Daemon you can write your own
gravity or attractor daemons, add friction to particles or
objects, bound forces or define falloffs. As youre dealing
with forces, represented as Vectors, some basic knowledge
of applied mathematics is recommended.
On page 36 and 58 you can also find two Scripted
Daemons, showing, how Vector forces are implemented.
The only limitation is that forces from Scripted Daemons
seem to act a little bit different from the predefined
Daemons in RealFlow.
Fig. 9: The Events Script window
Getting Started
RF_magazine
21
Scripting with RealFlow 02_2007
Another very powerful facility is the Scripted Emitter. With
this type its theoretically possible to write your own fluid
solver, but we dont want to go that far. Maybe youre
asking, why one should use a Scripted Emitter, though
RealFlow already makes a wide range of predefined
emitters available?
The answer to this question lies deep within the way,
RealFlow solves fluid dynamic equations. Maybe youve
already tried to animate viscosity or density of a particle
stream over time? If so, you might have encountered some
serious crashes. The fluid becomes instable and the
calculation suddenly aborts. With a Scripted Emitter you
can avoid this behaviour and define ranges for the param-
eters from an emitters Particles panel. This is truly a
mighty feature! The Scripted Emitter is not an indivdual
object you can choose from the emitter list. The Scripted
Emitter is available for each type, like Circle, Sphere, Spline
etc. All you have to do, is changing the type from Liquid to
Custom:
Emitter > Node Params > Particles > Type > Custom
After this change, a new button, named Edit appears.
To open the related scripting window, simply hit this
button.
The Custom type provides just one function to calculate
the forces:
computeInternalForces( emitter )
The last subtype is the Scripted RealWave object. Similar to
the Scripted Emitter, this object is also part of the different
RealWave types. You first have to add a new RealWave
surface and then place a new deformer via Add Wave.
Heres a screenshot of how you can access this feature:
Again, an Edit button appears, to open the scripting
window. There, youll also find just one function to update
the vertices of the RealWave object:
updateWave( vertices )
Please note, that only its only possible to alter the y
direction of the vertices. Translations in x or z direction will
be ignored. The best application for a Scripted RealWave is
to import greyscale images as displacement templates.
This technique is described in detail in RF_magazine Issue
01_2007 Cinema 4D Special Edition, page 40 et seqq.
Of course its possible to develop your own formulas to
create new, not implemented wave types.
Fig. 10. Parameter rollout for the Custom Emitter type
Getting Started
RF_magazine
22
Scripting with RealFlow 02_2007
Calling Emitters and Objects
Now your persistence and patience will be rewarded
finally, because youre about to make the first steps with
Python and RealFlow!
Start with a Circle emitter by choosing:
Edit > Add > Emitters > Circle
> Square
The Node window now shows a new object called Circle01.
To use this emitter within a script, its necessary to
address it and store it in a Variable. The Variable type,
were introducing here is a Scalar:
emitter = scene.getEmitter("Circle01")
emitter is the name of the Variable, scene tells Python to
look at the Nodes panel for an emitter named Circle01. The
basic keyword in this statement is getEmitter to fetch the
desired object.
Now add a Torus object. To get this item, we use a very
similar construction:
object = scene.getObject("Torus01")
In this case, the Scalar variable is object and with the
getObject keyword we have access to the Torus01 item
from your basic scene.
Of course, sometimes its required to change an objects
name. Lets say you want to change the name from
Circle01 to Water. To identify the emitter again, you also
have to alter the name in the script:
emitter = scene.getEmitter("Water")
The Variable name may also be subject to change. In many
cases theres more than one emitter and you need a more
meaningful name. As youve already learned, its always a
good idea to use significant names. Later were developing
a foam script. This type of script uses multiple emitters
and we have to distinguish them by name:
water = scene.getEmitter("Water")
foam = scene.getEmitter("Foam")
Please note that the names of the emitters have to be
identical with the names used in the Nodes window and
the names must be written between quotation marks.
Another possibility would be to declare Variables with
emitter names:
water_emitter = "Water"
foam_emitter = "Foam"
water = scene.getEmitter(water_emitter)
foam = scene.getEmitter(foam_emitter)
In this case were using the Variables as a substitute for
the real name from the Nodes window. To make Python
understand that we want to use a Variable with getEmit-
ter( ) and not a String (= sequence of characters), we dont
write quotation marks.
With objects and other items its the same procedure:
collission_object = scene.getObject("Torus")
animated_daemon = scene.getDaemon("MainAttractor")
main_camera = scene.getCamera("Camer01")
rope_constraint = scene.getConstraint("Rope01")
fluid_mesh = scene.getMesh("Fluid")
Your First Steps
RF_magazine
23
Scripting with RealFlow 02_2007
Accessing Particles
Lets stay with emitters for a while. As you certainly know,
RealFlow is a particle based software. Each emitter type
spills out a number of particles, dependent on the Resolu-
tion parameter. Through Python you have direct access on
each individual particle. But how is it possible to read out
values, like Velocity or Position for a single particle?
The basic idea is to gather all existent particles and loop
through this amount. Fortunately RealFlow knows various
methods to detect the total number of particles and
provides direct access.
A very common construction is to look for the first particle
and then go through the total amount. As long as therere
particles in the scene, the loop will be executed:
fluid = scene.getEmitter("Circle01")
particle = fluid.getFirstParticle()
while (particle):
do something here
particle = particle.getNextParticle()
Whats happening here? First you have to identify the
emitter Circle01. The statement fluid.getFirstParticle( )
means: Search for the first particle emitted from fluid,
actually from Circle01.
Now, the script starts looping through all existent parti-
cles. With this method its possible to get information
from each particle in your scene. While the loop is
executed, youre able to perform calculations or check and
compare values.
To seize the next particle, the script replaces the current
particle Variable with the ID of following particle. The
keyword for this action is getNextParticle( ).
In some cases its recommended to skip particles for
speeding up a simulation or for testing purposes.
On page 9 I was talking about the Modulus Operator %.
This is exactly what youll need for this purpose. With this
example Im also introducing a new principle - the if
clause. Youll learn more about this later (see p. 27).
The operation you have to perform is the following:
skip = 5
counter = 0
fluid = scene.getEmitter("Circle01")
particle = fluid.getFirstParticle()
while (particle):
counter = counter + 1
if (counter % skip == 0):
do something here
particle = particle.getNextParticle()
In this example the script counts the total number of
particles. With each particle the counter Variable will be
increased by 1. Theres also a short form available (please
see page 11) :
counter += 1
The result is exactly the same value you can see under
Node Params > Statistics > Emitted Particles
The Modulus Operator % tells you, whether the operation
produces a remainder or not. Only if theres no remainder
(in this case the result is exactly 0) the instructions below
the if statement will be executed. This means that the
script skips four particles, because with the fifth particle
the condition is true. The higher the skip value, the more
particles will be missed out. With a skip value of 1, the
condition is always true and all particles will be included.
Besides the while method, RealFlow also knows the
for ... in and the for ... in range loop. The first one is also
often used with particles and additionally with large
numbers of objects. Amongst others, for ... in allows you to
change object properties simultaneously.
Your First Steps
RF_magazine
24
Scripting with RealFlow 02_2007
The for ... in loop is often used with a defined number of
particles. You have to collect the wanted data first, and
then go through them one by one. Especially when its
necessary to gather colliding particles for foam genera-
tion, the for ... in method is applied.
fluid = scene.getEmitter("Circle01")
collided_particles = fluid.getParticlesColliding()
for particle in collided_particles:
do something here
As you can see from the code, you have to store the
desired particles in the Variable collided_particles.
RealFlow identifies those particles with getParticlesCollid-
ing. After the particles have been stored, the script can
read out the Variable, calling each particle individually.
With lots of particles, this method may become very RAM
consuming, because RealFlow makes a copy of the particle
set with all its values and settings.
This statement could also be read as: Perform a given
calculation for each particle from the collided_particle set.
The for ... in range method works similar to the for ... in
loop. The main difference is that you can define a stop
value for the loop. Lets say, you have a group of six
spheres, but the operation should only affect the first
three objects.
spheres = [S0,S1,S2,S3,S4,S5]
no_of_spheres = len(spheres)
stop = no_of_spheres / 2
start = 0
step = 1
for object in range(start, stop, step):
current_sphere = spheres[object]
do something here
Whooo! Whats going on here? Well, nothing special. I just
used another Data Type. Do you remember Lists? If not,
then please have a look at page 7 and 8 again. But dont
worry, because everythings explained here, too. The first
expression is a List, containing six entries - the spheres.
The Variables in this List are named S0 - S5. Since Lists
always start with 0, its better to adopt your names to this
circumstance and avoid trouble. The spheres in the Nodes
panel must wear exactly the same name.
You only want the first three objects to be affected, so you
have to find a stop value. To get the total number of List
entries, its necessary to determine the length. This value
is stored in no_of_spheres. The List carries six entries and
we need three spheres. So the stop value is:
6 / 2 = 3
Of course you simply could write stop = 3, but here I
wanted to illustrate how to deal with Lists and their
entries. And in some cases it might be useful to get start
or stop values directly from a List.
To read out the current_sphere, youre using the current
value of the loop as a reference on the List containing the
spheres:
current_sphere = spheres[object]
Since were starting with start = 0, in our example wed
get these expressions (actually we cant see this term, but
this is the way it looks like internally):
current_sphere = spheres[0] ( S0 )
current_sphere = spheres[1] ( S1 )
current_sphere = spheres[2] ( S2 )
After youve stored the sphere in a Variable, youll be able
to perform calculations. With current_sphere = spheres[3],
the loop ends. Id suggest to make a few tests with this
method to get a better understanding.
The step value indicates an increment. So with each
step the loop adds 1 to the current_sphere Variable.
The increment hasnt got to be 1 necessarily. Other Integer
values are also allowed, but with different steps, you
might have to change the stop value!
Your First Steps
RF_magazine
25
Scripting with RealFlow 02_2007
The List expression shows the names of all objects. On
page 8, I explained that names have to written between
quotation marks. In this case, the entries of spheres serve
as Variables, used by RealFlow to identify the objects in the
Nodes window. In RealFlow, quotation marks with Lists are
a rarely used.
spheres = [S0,S1,S2,S3,S4,S5]
This notation would cause a Syntax error:
spheres = ["S0","S1","S2","S3","S4","S5"]
The creation of loops is a crucial task, used with almost
any Events or Custom Script (e.g. Daemon or RealWave).
Even Batch Scripts often show loops, especially in conjunc-
tion with Lists.
Building Vectors
Vectors are one of the most important Data Types within
RealFlow. Theres already been a detailed introduction on
page 13 et. seqq. Here, youll learn how to use Vectors in a
script and how to extract a Vectors components.
All RealFlow calculations are done in 3D space. The posi-
tions of a particle, emitter or object can be described with
their x, y, and z coordinates. Starting at the origin of a
scenes coordinate system, these three values can be
considered as an address, where you can find the object.
So itd be possible to draw an arrow from the origin to the
objects position. This arrow is the graphical representa-
tion of a Vector. But not only positions can be written as
Vectors. This is also possible for forces or velocities.
The mathematical representation of a Vector is:
a = (x, y, z)
In RealFlow its possible to determine Vectors simply by
writing a series of three values to a Variable. The complete
Python statement for initializing a new Vector is:
my_first_vector = Vector.new(x, y, z)
In RealFlow its often required to extract the x, y, z
values or further calculations, sometimes you may also
just need the y or z component of a Vector. The elements
of a Vector are called Scalars:
position = (1.0, 3.3, 2.7)
Now we want to calculate new y and z positions for a
single particle. But how could this be realised? Theres got
to be a way to split the Vector into its elements. Indeed,
RealFlow knows such a method:
position_x = position.getX()
position_y = position.getY()
position_z = position.getZ()
Fig. 11: Velocity vectors in RealFlow
Your First Steps
RF_magazine
26
Scripting with RealFlow 02_2007
In most cases youll deconstruct a Vector, perform a
calculation, and then build a new Vector using the fresh
values. The process for performing this operation is finally
always the same. Similar to the loop methods, you just
have to remember a few fixed steps:
Events Script: ParticleNewPos.rfs
fluid = scene.getEmitter("Circle01")
particle = fluid.getFirstParticle()
while (particle):
# 1. Get global position data in Vector format
pos_g = particle.getPosition()
# 2. Get components from the position Vector
pos_x = pos_g.getX()
pos_y = pos_g.getY()
pos_z = pos_g.getZ()
# 3. Calculate the new coordinates individually
new_pos_y = pos_y + 0.01
new_pos_z = pos_z + 0.01
# 4. Build a new Vector
new_pos_g = Vector.new(pos_x, new_pos_y, new_pos_z)
# 5. Use the new Vector for position change
particle.setPosition(new_pos_g)
particle = particle.getNextParticle()
This is already a complete script. To show you whats
happening here, I want to deconstruct this little proggie.
The first part uses a well known method to loop through
the emitters (Circle01) particles.
The first step is to get vectorized value, in this case the
script reads out the global position Vector and stores it in
the pos_g Variable.
In a second step, the script disassembles the x, y, and z
components from the pos_g Vector.
The task in this program is to alter only the y and z values,
while the x component remains untouched. The new
position values have to be calculated individually as well,
and theyre based upon the old pos_y and pos_z values.
The new_pos_y, new_pos_z, and the unchanged pos_x
values provide a basis for the new global position vector.
The Vector.new statement puts everything together.
Of course this Vector has to be stored again in a Variable
(new_pos_g) for further use.
The last steps assigns the new_pos_g Vector to the
particles using a setPosition instruction. Youll learn more
about the get and set functions later. Here, just put up
with the fact that these instructions are responsible for
getting and setting positions.
The last line of the script simply calls the next particle.
The representation in RealFlow isnt very spectacular, but
the script implements most of the principles, introduced
so far.
Fig. 12: The result from the Events Script ParticleNewPos.rfs
Your First Steps
RF_magazine
27
Scripting with RealFlow 02_2007
If & Else
When youre writing a script, its often required to set
certain conditions or alternative ways. Differentiations can
be achieved with if and else statements. The Syntax of this
function is rather simple and well known from your daily
life: Im sure you love new and fast computers, but money
is always the obstacle for buying the latest killer machine.
You have to calculate to get the maximum out of the
available money. This example is well suited for an if-else
construction:
available_money = 2500
computer_price = 3200
if (available_money < computer_price):
cant buy computer
else:
go to computer store
In this case, the amount of available_money is less than
computer price. The condition for this example is true, and
youre not able to buy the new computer. But Variables
can change over time and some new decisions can be
made:
available_money = 2500
computer_price = 3200
grannies_gift = 750
if (available_money + grannies_gift < computer_price):
cant buy computer
else:
go to computer store
Here, Granny saved your day, because 2500 + 750 = 3250.
As you can see, comparisons are not only limited to one
Variable, its also possible to perform calculations within
the if clause.
Another, equivalent notation would be:
total_money = availabe_money + grannies_gift
if (total_money < computer_price):
cant buy computer
else:
go to computer store
You could also reverse the comparison:
if (total_money >= computer_price):
go to computer store
else
cant buy computer
As you can see from this example, if-else conditions are
mostly connected with comparisons. There are also ways
to compare more than one value:
ram = 200
my_money = 250
birthday_gift = 210
if (my_money >= ram or birthday_gift >= ram):
set max_particles to 50,000,000
else:
set_max_particles to 100,000
Its also possible to use an and statement. This is often
needed for setting a certain limit:
ram = 200
gpu = 350
if (ram <= 250 and gpu <= 400):
buy both articles
else:
buy only graphics board
Your First Steps
RF_magazine
28
Scripting with RealFlow 02_2007
Another effective method for multiple queries is the elif
statement. The elif expression is an abbreviation and
stands for else if. You can use (almost) as many elif state-
ments as you want to:
if mandatory once
elif optional mutliple
else optional once
This time you want to sell your old computer. The decision
chain could be like this:
offer = 475
retail_price = 500
min_price = 400
if (offer >= retail_price):
sell computer immediatly
elif (offer >= min_price and offer < retail_price):
contact customer for further negotation
else:
dont sell computer
In this case, the customer will be contacted, because offer,
lies between retail_price and min_price. elif statements
mostly need a second criteria to complete a decision.
Have a look at this example:
offer = 520
retail_price = 500
min_price = 400
if (offer >= retail_price):
sell computer immediatly
elif (offer >= min_price):
contact customer for further negotiation
else:
dont sell computer
The result from this example is ambiguous. The first
criteria is fulfilled, since 520 is greater than 500, but the
second condition is also true, because 520 is greater than
400, too. To avoid such an inconsistency, you have to
introduce a second requirement, like in the first example
on the left.
In Python (and all other programming languages) its
allowed to construct convoluted if-else-conditions:
offer = 540
retail_price = 500
zip = 90482
if (offer >= retail_price):
if (location == zip):
shipping_cost = 10
else:
shipping_cost = 25
else:
dont sell computer
With nested if-else constructions, its possible to assign
case dependent values to a Variable. Having a look at the
example above, youll recognize that the computer has
been sold (offer is greater than retail_price). The second
clause is used to determine, whether the customer is
located in your hometown or not. The shipping_cost value
directly depends on the customers location.
Your First Steps
RF_magazine
29
Scripting with RealFlow 02_2007
Introduction
You already know the basics of Python scripting in
RealFlow. Youre able to loop through an emitters parti-
cles, youve heard about building Vectors and else-if
clauses. The coming chapter is about getting and setting
values directly from RealFlow. This is the quasi-core of
RealFlows Python extension. With the get and set state-
ments, you have direct access to all properties of an
emitter, object, daemon etc.
The variety of get and set statements seems to be endless.
So its necessary to sort the instructions, to keep an
overview. In this issue, were dealing with the most
common get and set directives. RealFlow also provides
commands for getting and setting vertices, polygon faces,
animation keys, image pixels and more.
Youve already met a few statements:
getEmitter( )
getPosition( )
getFirstParticle( )
getNextParticle( )
These instructions had been used to find a scene emitter,
to have access to individual particles or make use of a
particles Vector position data. With different get instruc-
tions, youll receive different values and results,
e.g. Integers, Vectors, Floats and Strings.
The online manual shows a rather long index of different
get and set commands. These commands are written in a
particular format, like:
Emitter getEmitter( string )
Object getObject( string )
The first word describes the allowed object type, like
emitter, object (primitive), camera, mesh and so on. The
second part is the specific get command, available for
many parameters and RealFlow objects. The next expres-
sion determines the Data Type you have to use with this
particular instruction. The Data Type also specifies, how
the argument will be treated:
Particle getParticle( int )
This operation can only be used with particles and the
input value has to be an Integer. Using it with a daemon
and a string, like Gravity01, would cause an error.
The manual sometimes gives more hints:
Emitter [] getEmitters( )
The brackets [] tell you that the resulting Data Type will be
a List. Do you remember? With a Scalar its only possible to
store one value, while Lists can carry two or more entries.
All descriptions of the available commands are working
the same way, as the examples above. With a little experi-
ence, youll be able to forecast the used object and Data
Type, but if youre not sure, you can always refer to the
manual:
Help > Contents... (opens a new application) > Index
Help > Contents... > Contents > Scripting Reference
The second source contains a more structured compila-
tion, sorted by object types.
How to Use Get & Set
The concept behind the get and set statements is simple,
but very clever, because its directly connected to
RealFlows user interface. The commands share exactly
the same names you can see in the Nodes and Node
Params windows. The main difference is the number and
type of arguments, a get or set command needs. The
commands are not limited to loops, they can be used
anywhere.
Changing Attributes with Get & Set
RF_magazine
30
Scripting with RealFlow 02_2007
In this example, a Cone had been added to the scene.
The parameter rollout above contains everything you need
to access the objects properties. The most interesting
parameters are certainly located within the Node tab. Here
you can find basic attributes, like Position, Rotation or
Scale.
The online help describes the corresponding get
commands as follows:
any getParameter( any )
This notation tells us that we can use any object or Data
Type with the getParameter command. This operation is
not limited to primitives, emitters or cameras. Also the
arguments can be an Integer, Float, Vector etc.
The Data Type you receive with the getParameter
command can also be read out from the Node tab. When-
ever you see a triplet of values, the result will be a Vector:
Position 0.0 0.0 0.0
Rotation 0.0 0.0 0.0
Scale 1.0 1.0 1.0
Pivot 0.0 0.0 0.0
Please note: The individual values of the Vector are Floats.
Even Color is represented by three values for red, green
and blue (RGB). The Data Types for Simulation and Dynam-
ics are Strings. The fields can contain words or expressions:
Simulation Active
Dynamics No
If WetDry Texture was set to Yes, wed also have some
Integer values, like
@ resolution 256
The values for @ filter strength and @ ageing are Float
types again.
As already mentioned, its crucial to know, which Data
Type a get command spits out, because in the reverse
process with setParameter, you need exactly the same
Data Type again to alter the appropriate value. Let me
show you an example:
cone = scene.getObject("Cone01")
pos_global = cone.getParameter("Position")
The result is a Vector, consisting of three Floats. After a
calculation has been finished, you can build a new Vector.
This new Vector must carry three Floats (Integers are
allowed, too) again. You cant replace the Floats with
Strings. This would cause a Syntax error:
pos_new = Vector.new("left", "up", "right")
Changing Attributes with Get & Set
RF_magazine
31
Scripting with RealFlow 02_2007
The next example is a complete script, ready to use within
RealFlow. The program adds a value of 0.2 to the y position
of a Cone with each new frame:
Events Script: ChangeConeYPos.rfs
def onSimulationFrame():
cone = scene.getObject("Cone01")
# 1. Get global position data in Vector format
pos_g = cone.getParameter("Position")
# 2. Get components from the position Vector
pos_x = pos_g.getX()
pos_y = pos_g.getY()
pos_z = pos_g.getZ()
# 3. Add 0.2 to the y component
new_pos_y = pos_y + 0.2
# 4. Build a new Vector
new_pos_g = Vector.new(pos_x, new_pos_y, pos_z)
# 5. Use the new Vector for position change
cone.setParameter("Position", new_pos_g)
This script is very similar to the Events Script on page 26
(ParticleNewPos.rfs). There, the script adds values to the y
and z positions of all particles. Here, were just using a
single object. The process of getting, splitting and assem-
bling the Vectors is finally the same. The main difference
lies in the last statement:
cone.setParameter("Position", new_pos_g)
With the particle method, its just
particle.setPosition(new_pos_g)
Particles dont use the same get and set commands as
physical objects in RealFlow. For an emitter or a mesh, its
possible to see values directly in the corresponding Node
Params window. Position or Velocity values arent
displayed at all. Theyll be used internally. For faster access
and better distinction, the particle attributes dont use the
Parameter keyword.
And theres another difference: The getParameter
command only takes one argument, named Position. The
setParameter shows Position and new_pos_g. So, with
setParameter you have to tell RealFlow which type of
parameter you want to change (Position, Pivot, Rough-
ness...) and the new value using the correct Data Type.
The changing values can be monitored in the Node Params
window during the simulation. Just give it a try and watch
the y component of the Position value.
Tip
Dont copy and paste scripts to from the original source to
RealFlow. Different fonts, character sets and spaces or
blanks will cause errors almost any time. Its better to type
scripts manually and think about whats happening.
Fig. 13: The Cone performs a constant movement in y direction
Changing Attributes with Get & Set
RF_magazine
32
Scripting with RealFlow 02_2007
With getParameter and setParameter you can have lots of
fun, since you can alter, change or turn off and on almost
everything. Some tasks could also be solved manually and
with easy means, but scripting is much more accurate.
Especially with collisions its sometimes not easy to get
exactly the moment of the first interaction between two
objects or particles. With a Python script, the solution is
just a few lines of code away. This is the initial situation:
Our goal is to deactivate the objects, after theyve hit the
floor. All items have different positions. Deactivating them
manually could be an ungrateful job. This scene just uses
three objects, but imagine 20, 30 or 60 items.
According to the methods you have seen so far, we always
called objects individually, but with more than three items,
this could get rather annoying. To ease this task, RealFlow
provides mighty tools for object selection. The operation,
Im going to use here is named:
getSelectedNodes()
With this command, you just select the objects you want
to be affected by the script from the Nodes window.
RealFlow recognizes your selection and stores all items in a
List object. So the correct expression is:
items = scene.getSelectedNodes()
Just for remembrance: With a selection of the three
objects Cone, Cube and Cylinder, the entries of items
would be internally:
items = [Cone,Cube,Cylinder]
The next step is to collect all objects, colliding with the
Floor item. RealFlow also knows a function for this
purpose. The Data Type created with this command is
again a List, because theres more than one object to
collide with Floor:
floor = scene.getObject("Floor")
colliding_objects = floor.getCollidingObjects()
Now we have to browse through the list of collided objects
and deactivate the appropriate primitive:
if (colliding_objects != []):
for object in colliding_objects:
object.setParameter("Simulation","Inactive")
The if condition checks, whether the List colliding_objects
is empty or not. The expression
if (colliding_objects != []):
means: If the List colliding_objects is not (!= ) empty, then
do something. An empty List is written as []. Another idea
is to check the length of the List.
If a collided object has been detected and written to
colliding_objects, the Simulation parameter will be set to
Inactive. In other words: As soon as an object hits the floor
completely, itll be set to Inactive and the enclosed fluid
can be released. On the next page youll find the listing for
this script.
Changing Attributes with Get & Set
RF_magazine
33
Scripting with RealFlow 02_2007
Events Script: InactiveOnCollision.rfs
def onSimulationBegin():

items = scene.getSelectedNodes()
for object in items:
object.setParameter("Simulation", "Active")
object.setParameter("Dynamics", "Rigid body")
def onSimulationStep():
items = scene.getSelectedNodes()
floor = scene.getObject("Floor")
colliding_objects = floor.getCollidingObjects()
if ( colliding_objects != [] ):
for object in colliding_objects:
state = object.getParameter("Simulation")
if (state == "Inactive"):
pass
else:
object.setParameter("Simulation", "Inactive")
object.setParameter("Dynamics", "No")
The first function initializes the selected items and makes
them active. This is useful in case of starting multiple
simulation passes, because you dont have to switch back
each time you want to simulate the scene.
This program identifies all objects colliding with the floor
item and writes them to a List Variable:
colliding_objects = floor.getCollidingObjects()
The last if clause is just a little construction for checking,
whether the Simulation state has been changed. So if the
Simulation state is already set to Inactive, then the script
simply does nothing.
The only limitation is that the objects have to collide
completely with the floor item, before theyll be inacti-
vated. With the first contact the items remain active!
Fig. 14: Dissolved objects after collision
Changing Attributes with Get & Set
RF_magazine
34
Scripting with RealFlow 02_2007
Working with Particles
Until now, you have learned a lot about getting and
setting attributes from objects. As Ive already mentioned,
particles represent an own class and provide direct access
to special values and characteristics.
When you have to work with particles, its always neces-
sary to construct a loop function and call them individu-
ally. For this task, an emitter is required and you have to
distinguish between emitter and particle based properties.
The emitter attributes are directly displayed within the
Node Params window:
Changing these values may cause instabilities and a
crashing RealFlow application. You should alter these
values carefully. With a Scripted Emitter its safe to change
these values. But here I want to talk about an emitters
output: Particles.
While looping through an emitters particles, you have
access to a wide variety of attributes. The most common
values will probably be these two commands:
getVelocity( )
getPosition( )
Velocity and Position can be changed directly by creating a
new Vector after performing a calculation. Other values
are not meant to change, but they can be used for control
structures. With Density or Pressure, for example, youre
able to create foam, based on physical properties of a
fluid. This is a very effective way to create believable spray.
Here are a few important and often used instructions:
getMass( )
getDensity( )
getPressure( )
getNormal( )
getNeighbors( )
These attributes occur nowhere within a RealFlow
window, because theyre just meant for internal calcula-
tions. Heres how to use them:
emitter = scene.getEmitter("emitter_name_here")
particle = emitter.getFirstParticle()
while (particle):
particle.getVelocity()
particle.getPosition()
particle.getMass()
particle.getPressure()
...
particle = particle.getNextParticle()
In many cases its important to get an idea of a values
dimension. Is it large or small, a Float or an Integer?
Especially with if-clauses its crucial to know the magni-
tude. RealFlow knows a function for printing out any kind
of value:
scene.message(str(variable_name))
This statements directly prompts the appropriate value to
RealFlows Message window, but in some cases you might
get results like this:
RealFlow vector at <0xdee2>
Changing Attributes with Get & Set
RF_magazine
35
Scripting with RealFlow 02_2007
A result like the one from page 34, is displayed when you
try to print out the value of Lists, Vectors or Dictionaries.
Whenever theres more than one value stored in a
Variable, RealFlow displays the hexadecimal value. Only
the individual components of these multi-value Variables
can be printed out readable:
vec_z = vector.getZ()
entry = list[2]
scene.message(str(vec_z))
scene.message(str(entry))
The str command forces Python to transform any value
into a String. This is the only Data Type that can be used
with the Message window. Of course its possible to use
more than one Variable within a str term. True Strings,
defined by the user, have to be placed within quotation
marks:
vec_z = 3.5
entry = Cube04
scene.message(str("Values: "+vec_z+" / "+entry))
Result: Values: 3.5 / Cube04
With this little helper you can estimate dimensions and
adopt values for forces or limits. Please note that RealFlow
becomes significantly slower while printing out scene
messages!
A nice little application is a mass based stop function for
an emitter. The following script simply reads out the Mass
values from each particle and adds them up. If the total
mass is greater than a given threshold, then the emitter
stops pouring out new particles. Of course this script could
also depend on Pressure or Density.
As you can see, scripts dont have to be complicated to be
effective. Even with easiest means its possible to achieve
good results. With a daemon based approach, itd be
rather difficult to achieve a result like the one on the right.
Events Script: StopOnMass.rfs
def onSimulationStep():

total_mass = 0
mass_threshold = 1000
emitter = scene.getEmitter("Circle01")
speed = emitter.getParameter("Speed")
particle = emitter.getFirstParticle()

while (particle):
mass = particle.getMass()
total_mass = total_mass + mass
if (total_mass >= mass_threshold):
emitter.setParameter("Speed", 0.0)
particle = particle.getNextParticle()
The script again shows the basic concepts, e.g. gathering
the scene and particle data or creating a loop structure
Theres also an if condition to trigger a particular event.
If the total_mass Variable reaches a limit of 1000
(mass_threshold) the emitter speed is set to 0.0. In other
words: The emitter stops creating new particles. The good
thing with this script is that the mass of a particle
depends on its density. Setting Density to 100.0 in the
Node params window, will roughly produce 10x more
particles than a standard Density of 1000:
Changing Attributes with Get & Set
RF_magazine
36
Scripting with RealFlow 02_2007
Another possible application for particle manipulation are
Scripted Daemons. With daemons its possible to apply
external forces to particles. The following example applies
a height dependent force to a particle.
The first step is to create a Scripted Daemon:
Edit > Add > Daemons > Scripted
> Scripted
You could leave the default name Scripted01 or assign a
new one. In this example I decided to rename the daemon
and call it HeightDaemon.
Scripted Daemon: HeightDaemon.rfs
def applyForceToEmitter(emitter):
accel = -9.81
factor = 7.0
emitter = scene.getEmitter("Square01")
particle = emitter.getFirstParticle()
while (particle):
pos_g = particle.getPosition()
pos_y = pos_g.getY()
gravity = accel / ((pos_y * factor) + 0.001)
force = Vector.new(0.0, gravity, 0.0)
particle.setExternalForce(force)
particle = particle.getNextParticle()
OK, lets go through this script. The first part initializes the
needed variables, while accel is the acceleration of gravity,
and factor is a value to prevent the particles from slowing
down to fast. The smaller the value for factor, the stronger
the final force. The while-loop goes through the particles
from the Square01 emitter and checks the height perma-
nently.
Within the loop, the script calculates a height dependent
gravity force:
gravity = accel / ((pos_y * factor) + 0.001)
In this formula we met all the initial Variables again.
As you can see, the script performs a division here and you
have to take care that theres no division with Zero. This
may happen directly after the particles leave the emitter.
To avoid a Zero division, I simply add a small value of 0.001.
Due to the basic rules of calculation some brackets arent
absolutely necessary, but sometimes it can improve
readability. The expression:
((pos_y * factor) + 0.01)
could also be written as
(pos_y * factor + 0.001)
because * and / always have higher priority.
The next step is to construct the new Vector representing
a force in negative y direction. With the Scripted Daemon
specific command setExternalForce(force), the new force
will be applied.
Isnt that nice? With some easy and fully customizable
steps its possible to script your own daemon. Another
idea is to make the gravity force velocity dependent.
Conclusion
The possibilities with RealFlow and Python scripting are
almost endless. Examples, techniques or methods could
fill another 10 or more magazines, but here I just wanted
to give a first introduction into scripting. The following
projects are just a selection of whats possible. They range
from beginner to intermediate, but therell surely come an
issue of RF_magazine, covering advanced scripting...
Changing Attributes with Get & Set
RF_magazine
37
Scripting with RealFlow 02_2007
Whats a Graphical User Interface?
A Graphical User Interface (GUI) is a kind of environment in
software applications, providing user friendly tools for
interaction. Each time youre starting a computer
program, you can see a GUI. Palettes, buttons, toolbars,
and value fields are elements of such interfaces. Even
RealFlow provides a GUI - it consists of windows, param-
eter tabs and selection lists, filled with emitters, daemons
or constraints.
Youve already read a lot about Variables, like Scalars, Lists
or Vectors. Until now you had to define all Variables at the
beginning of the script, within a function or a loop.
What, if there was a possibility to let the user enter his
own starting values? You wouldnt have to change the
source code for altering some parameters.
RealFlow and Python offer functions to create those GUIs.
Of course theyre not as complex as GUIs you know from
software application, but you can open a window with
custom tailored values. These values will be taken over by
RealFlow and then used with your current script.
The Structure Of RealFlow GUIs
GUIs can be used with Batch and Events scripts, but theyre
mostly needed with Batch scripts. The usage of GUIs
within an Events Script is limited to the onSimulationBegin
and onSimulationEnd functions.
GUIs are constructed using a particular structure. This
structure is always the same and only contents are
changing. Another very important issue is the usage of
Data Types. As with all Variables, the correct specification
of the used Data Type is the premise for the execution of a
script. While defining a Variable, the Data Type is often
obvious from the way, youre writing it:
number_of_objects = 23
In this case the value apparently is an Integer. With GUIs
the situation is different, because you have to define
exactly the Data Type, youre expecting from the user.
So, its indispensable to make up your mind about Data
Types before you can start to build a GUI. But with a little
exercise youll quickly get a feeling for these Variables.
With RealFlow GUIs its not only possible to create your
own windows, you can also add custom error messages
and use file or node pickers. The most common application
surely is the form for custom and default values. The
process of working with those GUIs is divided into two
substeps:
1.. Creating the GUI with all fields and Variables you
want to provide to the user
2. Transferring the custom entries into values, used by the
script
The very first step is to define a new Variable containing
the GUI dialogue window:
form = GUIFormDialog.new()
This simple statements initializes the window. The
dialogue is also called a modal window. This type of
window always comes to the fore, while the contents of
the main application arent accessible, as long as the
modal window is open.
Your next job is to define the user Variables. RealFlow
knows statements for (almost) each Data Type:
form.addIntField()
form.addFloatField()
form.addVectorField()
form.addListField()
form.addBoolField()
form.addStringField()
The only Data Type missing here, is the Dictionary.
Creating Graphical User Interfaces
RF_magazine
38
Scripting with RealFlow 02_2007
Lets have a closer look at these add statements. Each
statement expects a series of arguments, defining the
basic information for the user:
The fields name
The default value
addListField( ) also needs the List entries, e.g. for the
particle type, like Dumb, Liquid, Gas, Elastic or Custom.
Lists have to be defined, before they can be used with a
GUI instruction. Another requirement is the usage of
unique field names, you cant share the same name for
two or more fields.
To give you a first impression, of how GUIs are created,
heres an example for setting up a basic scene emitter
with a Batch Script:
form = GUIFormDialog.new()
e_types = ["Circle","Square","Sphere","Triangle"]
p_types = ["Liquid","Dumb","Elastic","Gas","Custom"]
form.addListField("Emitter", e_types, 0)
form.addListField("Type", p_types, 0)
form.addFloatField("Resolution", 1.0)
form.addFloatField("Density", 1000.0)
form.addFloatField("Viscosity", 3.0)
form.addStringField("Name","BasicEmitter")
As you can see from this example, the List Variables are
used within the addListField( ) statement, also all non-
variables are written between quotation marks, both in
the Lists and the argument brackets.
This script snippet wont work in RealFlow so far, because
some functions are still missing. But theres a preview on
the right, of how the resulting window will look later. You
might notice that the field names arent arranged in the
order you defined within the GUI creation block. Unfortu-
nately RealFlow sorts the fields alphabetically, but this
aspect wont impact the GUIs functionality.
The second step for a fully working GUI is the assignment
of Variables, used by the script. Its necessary to tell the
script, what should happen with the values, the user
entered so far. A transformation like that is normally
introduced with an if-clause:
if (form.show() == GUI_DIALOG_ACCEPTED):
The term GUI_DIALOG_ACCEPTED is a fixed expression,
determining that the user hit OK. The opposite of this term
is GUI_DIALOG_REJECTED. Now, you have to transfer the
values into Variables. The key instruction for that purpose
is written this way:
variable_name = form.getFieldValue()
The getFieldValue( ) statement also needs an argument
and this is exactly the fields name.
emitter_type = form.getFieldValue("Emitter")
particle_type = form.getFieldValue("Type")
resolution = form.getFieldValue("Resolution")
density = form.getFieldValue("Density")
viscosity = form.getFieldValue("Viscosity")
name = form.getFieldValue("Name")
Here its obvious why its not allowed to use shared field
names, because the result would be ambiguous. Variables,
created during this process can now be used like any other
Variable before. Together, these instruction sets now open
a new modal user window.
Creating Graphical User Interfaces
RF_magazine
Scripting with RealFlow 02_2007
Batch Script: CreateBasicEmitter.rfs
# Create new modal window and define fields
form = GUIFormDialog.new()
e_types = ["Circle","Square","Sphere","Triangle"]
p_types = ["Liquid","Dumb","Elastic","Gas","Custom"]
form.addListField("Emitter", e_types, 0)
form.addListField("Type", p_types, 0)
form.addFloatField("Resolution", 1.0)
form.addFloatField("Density", 1000.0)
form.addFloatField("Viscosity", 3.0)
form.addStringField("Name","BasicEmitter")
# Transform user values into variables
if (form.show() == GUI_DIALOG_ACCEPTED):
emitter_nr = form.getFieldValue("Emitter")
particle_nr = form.getFieldValue("Type")
resolution = form.getFieldValue("Resolution")
density = form.getFieldValue("Density")
viscosity = form.getFieldValue("Viscosity")
name = form.getFieldValue("Name")
# Get the emitter/particle type from the lists
emitter_type = e_types[emitter_nr]
particle_type = p_types[particle_nr]
# Create emitter using the new variables
emitter = scene.addEmitter(emitter_type)
emitter.setParameter("Type", particle_type)
emitter.setParameter("Resolution", resolution)
emitter.setParameter("Density", density)
emitter.setParameter("Viscosity", viscosity)
emitter.setName(name)
The first two parts have already been explained on p. 38,
while the creation process of the emitter is just the usage
of various get and set instructions. Instead of real names
and numbers, like Circle, 1000.0 or 3.0, were just using
Variables. For better understanding, Id like to explain the
way of a value through the entire script:
1. Add a field, named Resolution with a default of 1.0
2. The user enters a value of 5.0
3. The scripts fetches the value 5.0 from the GUI window
4. The value 5.0 is assigned to the resolution variable
5. The value 5.0 is used for the setParameter statement
6. The script inserts 5.0 for Resolution in Node params
The List objects are bit tricky, but there shouldnt be
anything new to you. The addListField( ) statement is not
working with strings, but with indices. Instead of
form.addListField("Emitter", e_types, "Circle")
the argument needs an index for identifying the correct
type from the e_types List. Since Lists always start with 0
by default, the term
form.addListField("Emitter", e_types, 0)
gets the correct type for the GUI window. The problem is
that the GUI also assigns 0 to this expression:
emitter_nr = form.getFieldValue("Emitter")
We need a string for this expression, not a number,
because there is no emitter type called 0:
emitter = scene.addEmitter(emitter_type)
With this expression we get a String from the e_types list:
e_types = ["Circle","Square","Sphere","Triangle"]
emitter_type = e_types[emitter_nr]
emitter_type = e_types[0]
39
Creating Graphical User Interfaces
RF_magazine
Scripting with RealFlow 02_2007
Like looping through particles, the creation of GUIs always
follows the same rules and needs practice.
File and Node Pickers
The last section of this chapter is about opening files and
selecting Nodes. RealFlow knows two instructions for this
issue, both are pretty similar:
files = GUIFilePickerDialog.new()
path = files.show( FILE_PICKER_LOAD, "/Users/tsn/RF",
"*.jpg;*.tga;*.tif", "Load Images" )
The keyword in this term is FILE_PICKER_LOAD, for saving
issues itll be FILE_PICKER_SAVE. The next expression
specifies the path to the appropriate directory you want to
open, followed by the file extension, youre looking for.
The last phrase indicates the window name.
The file format can be extended to various other format.
Imagine a series of pictures you want to use inside
RealFlow. These images can be stored in JPG, TGA or TIF
format, other formats arent allowed:
"*.jpg;*.tga;*.tif"
If you dont want to restrict the file type, just use the
common wildcard notation "*.*"
The Node Picker also opens a window, showing all entries
from the Nodes window. From this window a multiple
selection can be made and the chosen objects will be used
for your script. The result of this operation is a List, where
all names are stored.
dialog = GUINodesPickerDialognew()
nodes = dialog.show( TYPE_EMITTER | TYPE_DAEMON )
for node in nodes:
scene.message(node.getName())
This example is directly taken from RealFlows online help
and shows the construction of this method. With the TYPE
expression, its possible to allow only certain object types,
used in RealFlow. In detail these types are:
TYPE_DAEMON
TYPE_OBJECT
TYPE_EMITTER
TYPE_MESH
TYPE_CONSTRAINT
TYPE_REALWAVE
ALL_TYPES
The delimiter between the TYPE keywords is the so called
pipe character, which can be found pressing AltGr+ > (Win)
or Alt + 7 (OS X).
I always recommend to experiment with these GUI types
to find out, how things are working. Especially for begin-
ners its often very confusing, which Data Type to use and
how to deal with List entries. You can start with simple
scripts, based on the program from page 39 and then carry
on with more difficult GUIs.
40
Creating Graphical User Interfaces
RF_magazine
41
Scripting with RealFlow 02_2007
Project Overview
The creation of foam and spray is one of the most wanted
and discussed subjects with RealFlow. Meanwhile there
are already some fantastic approaches available, creating
different types of foam particles for various needs.
Despite this fact, Id like to start with foam creation,
because its a very good example for the implementation
of the methods and concepts, youve learned so far. Over
the next pages, I want to evolve and develop the foam
script step by step, to show you exactly what happens.
The Nature of Foam and Spray
Foam consists of little gaseous blisters, separated through
walls. To achieve the formation of walls, a tenside is
needed to reduce the fluids surface tension. Foam is a
dispersion of air or gas in a medium, containing tensides.
There are some natural foam producing processes, e.g.
through alges or fermentation.
Spray is of very similar nature, but a tenside is not neces-
sarily needed. On ocean surfaces, spray normally occurs,
when air and water are merging. A good example is a
breaking wave. Also the dispersion of tiny waterdrops in
air creates spray. Foam and spray arent fixed states, they
can shade off into each other.
The white appearance of spray and foam is a result of light
dispersion, where parts of the light spectrum are differ-
ently scattered. This effect is known as Rayleigh-Dispersion
and describes the dispersion of electromagnetic waves on
spherical particles.
What does this all mean for the creation of spray with
RealFlow? Considering the colour of foam, we can see that
this is mainly a matter of texturing and rendering. Also the
physical or chemical processes are hardly to achieve, but
therere some properties, we can use for simulating foam.
1. The creation process may depend on pressure, age or
velocity (breaking wave, stormy wind)
2. Spray can consist of small particles dispersed in air
3. Foam bubbles of different size normally accumulate
to bigger structures
4. The dispersion of air in water causes a change of the
specific weight - foam is lighter than water
5. Spray and foam can reform to water and vice versa
6. Spray and foam consist of large particle amounts
Thats already a lot of information for our project. Now we
have to find a way to implement these features into a
RealFlow script. Python for RealFlow provides most of the
physical properties, listed in the compilation above. The
third topic is certainly the biggest problem, because its
not easy to calculate the internal forces between particles.
Wed need a Scripted Emitter for this purpose and, of
course the appropriate formulas.
The Foam Project
Fig. 15: Spray from breaking waves
RF_magazine
42
Scripting with RealFlow 02_2007
Creating Foam
As you can see, the foam creation process may depend on
various properties: Pressure, velocity, density and age.
Secondary factors can be mass and the amount of neigh-
bour particles or isolation time. Isolated particles wont
always appear like foam or spray.
Of course we need a particle source. To separate the water
particles from the foam particles, its recommended to use
a second emitter, where we can store the generated spray.
Now, the first challenge is clear: Declare a threshold value
for the creation of foam and separate spray from water
particles. The most obvious parameter seems to be a
certain velocity.
Velocity is a Vector and consists of a x, y, and z component,
but its not advisable to check the particle speed against
these three values. Since each Vector has a certain length
representing its strength, we better use this Magnitude
(see p. 14, 15).
Reminder
Calculating the Magnitude of a Vector:
vel = (3.2, 2.8, 1.5)
| vel |= sqr(vel
x
2
+ vel
y
2
+vel
z
2
)
| vel | = sqr(10.24 + 7.84 + 2.25)
| vel | = 4.51
RealFlow provides an easy way to extract the Magnitude
without performing any additional calculations:
vel.module( )
This function is simply added to the given vector and
automatically returns the Magnitude. You can study the
accurate usage in the first foam script on the right.
Each particle carries a specific ID. While looping through
the particles, the ID is used to differentiate, whether its a
water or a spray particle.
Events Script: Foam_001.rfs
def onSimulationStep():
threshold = 2.6
water = scene.getEmitter("Water")
foam = scene.getEmitter("Foam")
particle = water.getFirstParticle()
while (particle):
vel = particle.getVelocity()
vel_mag = vel.module()
pos = particle.getPosition()
id = particle.getId()
if (vel_mag > threshold):
foam.addParticle(pos,vel)
water.removeParticle(id)
particle = particle.getNextParticle()
Therere a few things to consider, when setting up the
RealFlow scene. The emitters must share the same setting
for Resolution. The foam emitter isnt meant to produce
particles, its just an empty container for the spray. So the
Speed value for foam has got to be 0.0. The colour for
water has been set to blue, for foam to white for better
distinction.
Fig. 16: The scene setup for Foam_001
The Foam Project
RF_magazine
43
Scripting with RealFlow 02_2007
The Syntax of the script Foam_001.rfs should already look
familiar to you. Weve just used the concepts, discussed so
far. The only new section is the following expression:
if (vel_mag > threshold):
foam.addParticle(pos,vel)
water.removeParticle(id)
With the first statement, the script adds particles to the
foam emitter, based on the current Position pos and
Velocity vel from the water particle. In a second step, the
same particles are removed from the water emitter, and
the separation of foam and water has been carried out.
All this only happens, if the Magnitude vel_mag of the
Velocity Vector is greater than the threshold value.
By having a look at Fig. 17, you can see that the spreading
of the white foam particles is rather uniform. Maybe there
are other properties leading to more random results. The
next idea would certainly be Pressure. So, with the next
script, we compare the Pressure value to a given threshold,
instead of Velocity. Since Pressure is a Float type, theres no
need for calculating the Magnitude again. But we still
need the Velocity Vector for the addParticle( ) function.
Events Script: Foam_002.rfs
def onSimulationStep():
threshold = 10000
water = scene.getEmitter("Water")
foam = scene.getEmitter("Foam")
particle = water.getFirstParticle()
while (particle):
pres = particle.getPressure()
vel = particle.getVelocity()
pos = particle.getPosition()
id = particle.getId()
if (pres > threshold):

foam.addParticle(pos,vel)
water.removeParticle(id)
particle = particle.getNextParticle()
The concept is pretty much the same, but the result
significantly differs from Fig. 17. We get a more random
look and the spray has a natural appearance.
Fig. 17: Velocity based creation of foam with Foam_001.rfs
Fig. 18: Pressure based creation of foam with Foam_002.rfs
The Foam Project
RF_magazine
44
Scripting with RealFlow 02_2007
The scripts Foam_003 and Foam_004 use Density and
Mass for foam generation. I continue without the listings
here, they can be found in the appropriate folder:
The_Foam_Project > Scripts
Instead of
pres = particle.getPressure()
were using these terms:
dens = particle.getDensity()
mass = particle.getMass()
Here are the results from these approaches:
The density method also yields to nice simulations with
randomly spread foam particles, but from this simple
approach its not apparent, whether this parameter will
stand our needs for transforming foam back to water.
The worst method is surely the mass idea. Theres no
change in the particles mass over time and the result is
that all particles are either foam or water. With all these
tests, we found out the working ones for later usage.
Now, that we know, how to shift particles from one
emitter to another, we can care about bringing foam
particles back to water. For this purpose, Age will play an
important role, but also Velocity and Pressure. If we could
observe a single particle, wed see that both Velocity and
Pressure will decline with increasing time. RealFlow
simulates these effects physically correct, so theyre suited
for our purposes.
Its a good idea to use a third emitter, storing the new
water particles. An extra colour helps to visualize the fresh
particles. To avoid an uniform or artificial look, it would be
great to have a random factor, we can add to the particles
age. Indeed theres a way to do so, by using Modules.
A Module is an external extension to enhance Pythons
capabilities. Therere lots of different Modules, but the
most common with RealFlow are certainly random and
math. While random is quite obvious, math provides us
with functions, like sine, cosine, square root, hyperbolic
sine and so on. The Syntax for importing Modules is:
import module_name
For random the complete notation is:
import random
Fig. 19: Density based creation of foam with Foam_002.rfs
Fig. 20: Mass based creation of foam with Foam_003.rfs
The Foam Project
RF_magazine
45
Scripting with RealFlow 02_2007
Events Script: Foam_005.rfs
def onSimulationStep():
import random

threshold_pres = 5000
water = scene.getEmitter("Water")
foam = scene.getEmitter("Foam")
rewater = scene.getEmitter("ReWater")

# Go through water particles

particle_water = water.getFirstParticle()
while (particle_water):
pres_water = particle_water.getPressure()
vel_water = particle_water.getVelocity()
pos_water = particle_water.getPosition()
id_water = particle_water.getId()
if (pres_water > threshold_pres):
foam.addParticle(pos_water,vel_water)
water.removeParticle(id_water)

particle_water = particle_water.getNextParticle()
# Go through foam particles, check age and pressure
particle_foam = foam.getFirstParticle()
while (particle_foam):
current_age = particle_foam.getAge()
random_age = random.uniform(-0.1,0.1)
threshold_age = random_age + 0.5
if (current_age >= threshold_age):
pres_foam = particle_foam.getPressure()
vel_foam = particle_foam.getVelocity()
pos_foam = particle_foam.getPosition()
id_foam = particle_foam.getId()
if (pres_foam < threshold_pres):

rewater.addParticle(pos_foam,vel_foam)
foam.removeParticle(id_foam)
particle_foam = particle_foam.getNextParticle()
The script is getting longer, but were still using the same
principles. The second loop simply turns around the
conditions from the first one. Were just using the foam
particles to convert them back into water. To get a realistic
behaviour, were introducing an Age dependency:
current_age = particle_foam.getAge()
random_age = random.uniform(-0.1,0.1)
threshold_age = random_age + 0.5
The only new method here is the definition of the
random_age Variable. The random.uniform(-0.1,01)
expression tells Python to create a random Float number
between -0.1 and 0.1. To define a threshold_age, the
random_age value is added to 0.5. With this method,
the particles turn back into water after 0.4 to 0.6 seconds,
but only if the pres_foam value is less then 5000.
The particles have to meet two criterias to transform into
water again, one of them is semi-random. So, this script is
also a very nice example for convoluted if-conditions (see
page 28).
Fig. 21: Result from Foam_005.rfs showing different states
The Foam Project
RF_magazine
46
Scripting with RealFlow 02_2007
Controlling Values Through Objects
At the moment we have two values influencing the
creation of foam and water: Pressure and Age. Each time
we want to change these values, wed have to open the
Events Script window, and enter new parameters.
Wouldnt it be nice to have another method for this task?
One idea would be to control Pressure and Age directly via
Null objects. With this method, the script works as if you
had sliders to adjust values.
What do we need for this function? First, we add two
Nulls, called Control_Pressure and Control_Age:
> Null
Edit > Add > Objects > Null
For controlling the values directly through the objects,
we have to read out the Nulls height or y positions. The
positions will be stored into Variables, used to define the
threshold parameters. The script doesnt care, whether
you move the objects or enter values within the Node
Params window.
cp = scene.getObject("Control_Pressure")
ca = scene.getObject("Control_Age")
pos_g_cp = cp.getParameter("Position")
pos_y_cp = pos_g_cp.getY()
pos_g_ca = ca.getParameter("Position")
pos_y_ca = pos_g_ca.getY()
threshold_pres = pos_y_cp * 2000
[ ... ]
if (pos_y_ca < 0):
threshold_age = -1 * (pos_y_ca / 2.0) + random_age
else:
threshold_age = (pos_y_ca / 2.0) + random_age
This is an effective method of control, easy to implement.
Lets have a quick look at the Python code:
Get the Null objects.
Assign Variables and derive the y position (Height).
Use the current position together with a factor for the
threshold_pres value. Pressure can be negative, so
negative positions are also allowed.
Age cant be negative and we have to multiply the
resulting value with -1 to make it positive, if needed.
Perform a simple calculation to get a smaller value, e.g.
with pos_y_ca = 1.0, the threshold_age value will be
0.5 + random_age
You can find the complete listing here:
The_Foam_Project > Scripts > Foam_006.rfs
To visualize the slider positions, I used small cubes here,
instead of Nulls. Of course, you can also add other objects,
but dont forget to make them invisible to the emitters!
Fig. 22: Object driven threshold values
The Foam Project
RF_magazine
47
Scripting with RealFlow 02_2007
Collision Based Foam Creation
Until now, we just had to care about emitters, no other
elements were populating the scene. For simulations its
much more realistic to add objects for fluid-body interac-
tion. This means that we have to create foam particles,
based on collisions. Fortunately RealFlow provides several
collision detection facilities to ease our job. The one, were
interested in, is this function:
getParticlesColliding( )
The name indicates that this expression gathers all
collided particles. The result has to be a List, containing
these particles. For reading out Lists, the appropriate
looping method is the for ... in approach (see p. 24).
The fundamental instruction block for getting the wanted
particles is written this way:
emitter = scene.getEmitter("Water")
collided_particles = emitter.getParticlesColliding()
for single_particle in collided_particles:
perform checks and calculations...
Again, were developing our foam script step by step. First,
lets check the result with a very basic scene. Just add the
two emitters Water and Foam and a collision object of
your choice.
EventScript: CollisionFoam_001.rfs
def onSimulationStep():

threshold_pres = 5000
water = scene.getEmitter("Water")
foam = scene.getEmitter("Foam")

# Go through water particles
collided_particles = water.getParticlesColliding()
for single_particle in collided_particles:
pres_water = single_particle.getPressure()
vel_water = single_particle.getVelocity()
pos_water = single_particle.getPosition()
id_water = single_particle.getId()
if (pres_water > threshold_pres):

foam.addParticle(pos_water,vel_water)
water.removeParticle(id_water)
As you can see, theres not much difference between our
first foam script and the one listed here. Its really just the
method of looping through the emitters particles, all the
other functions remain untouched. This is an easy and
effective way to call collided particles via Python, and
existing scripts can be quickly adapted.
The Foam Project
RF_magazine
48
Scripting with RealFlow 02_2007
The last approach was Pressure dependent, but of course
its also possible to write a collision based script, using a
particles Velocity. Maybe, a combination of both param-
eters leads to more realistic results? Another topic is that
foam is lighter than water. This behaviour makes foam
particles floating to the surface. As we can see from the
current results (Fig. 23), the foam is even located at the
walls of our vessel. Thats true for foam creation, but after
a while, the foam should disappear from there. We already
introduced an Age dependency. This method could be used
here, too, along with some random values.
What do we need for the next script?
Velocity check (Magnitude)
Height check
Age check
Pressure check
The foam particles on the top of the water surface should
disappear slower than the ones created below the water
line. We surely need a couple of if-conditions, but the more
effects were considering, the better the result will be.
Of course this is also a matter of speed and some methods
are simply not suited for tight production deadlines.
Until now, all emitters had the same parameters. With this
script, were starting to use different Density values for
Water, Foam and ReWater. All these actions will help to
achieve a better mixing of foam and water and keep the
foam particles away from the bowls bottom.
The listing for this script can be found here:
The_Foam_Project > Scripts > CollisionFoam_002.rfs
Again, were working with Nulls to adjust the threshold
values for Pressure, Velocity, Age, and Height. All in all we
now have a fully customizable and controllable foam
script. Another advantage is that the calculations will be
performed rather fast.
Here you can find the main routine for controlling the
amount of foam particles:
# Define height dependent life span of a particle
if (pos_foam_y >= threshold_height):
threshold_age = (threshold_age + 0.85) + rand_value
else:
threshold_age = (threshold_age + rand_value) / 2.0
# Transform particles back to water
if (current_age >= threshold_age):
id_foam = particle_foam.getId()
rewater.addParticle(pos_foam_g,vel_foam)
foam.removeParticle(id_foam)
With the first if-condition, we define the life span
(threshold_age) dependet from the current height
(pos_foam_y) of a particle. Particles below a particular
height (threshold_height) will transform back to water
quickly, other remain longer as foam. Based upon the
resulting Age value, the particles are shifted to the ReWa-
ter emitter and behave like fluid again. Together with
different densities, the result is realistic foam generation
with slowly disappearing particles.
Fig. 23: Foam particles at the walls of the vessel
The Foam Project
RF_magazine
49
Scripting with RealFlow 02_2007
As you can see from the image sequence above, the first
colliding particles are generating foam (white). After a
short period, most of these particles recycle to water (red)
again. Different Density values make the foam drift to the
water surface. During their way, theyre also transformed
back to water. The blue colour indicates original particles
from the emitter without any change of state.
Optimizing the Script
The last step is an optimization of the code and a function
for faster calculation. As I already mentioned, this a guide
for beginners, therefore Id like to show methods. This also
implicates that some statements in the script can be
shortened. I dont want to go into depth of fast Python
programming, but there are a few possible enhancements.
If youre interested in these refinements, just open both
CollisionFoam_002.rfs and CollisionFoam_003.rfs and
compare the source code. Youll see that some statements,
defined in separate Variables before, have disappeared.
Another improvement, especially for test simulations is to
skip particles. This sounds familiar to you, doesnt it?
Of course it does, because Ive been talking about this
topic before (see p. 23). The Modulus Operator, written as a
percent character %, is the key to success. This method is
also an effective way to control the amount of particles in
general. Here we go:
First, we need a counter and an Integer, defining how
many particles should be skipped. This Variables have
to be initialized at the very beginning.
def onSimulationStep():

import random
counter = 0
skip_value = 5
[ ... ]
Together with the first loop for foam generation, we
introduce the Modulus operation. Modulus tells us,
whether theres a remainder or not, while dividing
Integers (Floats are allowed, but may lead to weird results).
If the remainder is exactly Zero, the condition is fulfilled
and the particle can be transformed into foam. For all
other counter values, the condition is false and the foam
generation will not be performed.
Fig. 24: Image sequence produced with CollissionFoam_002.rfs
The Foam Project
RF_magazine
50
Scripting with RealFlow 02_2007
# Go through water particles
collided_particles = water.getParticlesColliding()
for single_particle in collided_particles:
counter += 1
if (counter % skip_value == 0):
pres_water = single_particle.getPressure()
vel_water = single_particle.getVelocity()
pos_water = single_particle.getPosition()
random_vel = random.uniform(-0.15,0.15)
if ((pres_water > threshold_pres) or
(vel_water.module() > threshold_vel + random_vel)):
foam.addParticle(pos_water,vel_water)
water.removeParticle(single_particle.getId())
The last enhancement is pretty easy: Just switch the foam
emitter type from Liquid to Dumb. This may also save lots
of time.
Of course therere many ways to improve the foam script,
but the basic aim was to show that you can already
achieve sophisticated and stunning effects, just by apply-
ing simple methods. Of course, you have to make up your
mind, before you can start writing a script, but thats
nothing unusual. Thorough preparation, a little patience
and some extra time for optimization will lead to the
desired results.
Threshold Parameters
These values control the amount of foam in our scene.
It may take a little time to adjust them, but with growing
experience youll find the settings for a specific scene
much faster. The adjustment through Null objects is not
necessarily needed - this method mainly has been intro-
duced for convenience and for educational purposes.
The thresholds normally have to be set individually for
each new scene. Its also up to you to introduce new
values and parameters for different results.
Random values also play a very important role, because
they care for a natural look. Adjusting these parameters
may also need some experimenting.
Credits
The creation of foam and spray has been widely discussed
throughout forums and scripting resources. Similarities
cant be excluded, though the programs in this edition of
RF_magazine have been developed independent from
other sources. Nevertheless Id like to credit to all the
smart people, who have published scripts so far:
Beatriz Lorenzo, Robb Flynn, Richard Sutherland, and of
course all other RealFlow enthusiasts who have written
neat foam scripts.
The Foam Project
RF_magazine
51
Scripting with RealFlow 02_2007
RF_magazine
52
Scripting with RealFlow 02_2007
Project Overview
The freeze function is a versatile and powerful feature that
can be used for numerous applications. The first part
offers some basic approaches with the freeze function.
Later, well develop a more complex script for freezing and
melting a particular shape, consisting of particles. Well
use animated helpers objects to control the melting speed
As always, the goal is to show you principles and methods,
and encourage you to write your own scripts or extend the
examples from RF_magazine.
Freezing Time and Particles
The Bullet Time effect, known from the famous Matrix
trilogy has a very special appeal. In brief terms, the Bullet
Time effect allows the artist to stop the motion of objects,
characters or particles, while the camera/spectator is still
moving. With RealFlow 3 thered been several more or less
lavish methods to achieve this effect, but with RealFlow 4
and Python scripting its a pure pleasure. The problems
always been to make RealFlow writing out BIN files, while
the particles stop moving. This could be done with a few
lines of code:
Events Script: BulletTime_001.rfs
def onSimulationStep():
emitter = scene.getEmitter("Circle01")
frame = scene.getCurrentFrame()
particle = emitter.getFirstParticle()
while (particle):
if (frame == 25):
particle.freeze()
if (frame == 60):
particle.unfreeze()
particle = particle.getNextParticle()
Maybe this script is the easiest application of freeze and
unfreeze, but the results are stunning. The convenient
issue with this script is that the emitter also stops spilling
particles during the deadlock time.
Lets have a quick look at the listing. The main part lies
within the while loop. Here were defining a frame range,
starting at 25 and ending with frame 60. The length of this
range could be defined as long as needed. During this time
span, the particles wont move. With frame 60 theyll be
released and continue flowing. Another possibility is to
setup a simple to GUI for user defined values:
Events Script: BulletTime_002.rfs
def onSimulationBegin():
form = GUIFormDialog.new()
form.addIntField("Start Frame", 25)
form.addIntField("Stop Frame", 60)
# Transform user values into variables
if (form.show() == GUI_DIALOG_ACCEPTED):
start = form.getFieldValue("Start Frame")
stop = form.getFieldValue("Stop Frame")
def onSimulationStep():
emitter = scene.getEmitter("Circle01")
frame = scene.getCurrentFrame()
particle = emitter.getFirstParticle()
while (particle):
if (frame == start):
particle.freeze()
if (frame == stop):
particle.unfreeze()
particle = particle.getNextParticle()
Fun with Freeze
RF_magazine
53
Scripting with RealFlow 02_2007
Please note that the scripts introduced here only work for
particles. With objects, like cubes, spheres or items from
SD files, we need another method, like the one from
RF_magazine Issue 01/2007 Cinema 4D Special Edition,
page 30.
Circular Particle Freezing
Now, that we know how to use the freeze function, we
could try to animate this effect. Our scene setup consists
of just a few objects: An emitter and a cube, serving as a
ground floor and some obstacles to make the simulation a
bit more interesting.
The idea is to define a circle, shrinking towards the centre
of the scene. The particles outside the borders of this circle
will freeze, while the inner particles are still flowing. To
control the speed of the desired effect and the radius of
the circle, its recommended to introduce a little helper
object, e.g. a Null or a Cube object.
Were just reading out the particles position and the
current x position of the helper object and compare these
values. From the moment, the particles are outside the
resulting value, theyll be frozen. To get the correct
particle position, we need the Magnitude again.
The example scene is orientated in Cinema 4D/Lightwave
style. This means, the y axis is in up orientation (YXZ). In
our case, we only check for a 2D expansion of the particles
and were not interested in any height information.
The question is, how do we compare the current position
pos of a particle with the radius r of our imaginary circle?
The solution is Pythgoras theorem for calculating a
Vectors Magnitude (see p. 15). It makes no difference,
whether were extracting the Magnitude from a Vector in
2D or 3D space. Thats our formula:
c
2
= a
2
+ b
2
c = (a
2
+ b
2
)
After we received the value for c, which is a Scalar, we
simply compare it against r and already got the condition
were after. In a circle, the radius remains constant at each
point of the outline, so we dont need further checks.
r
x
z
a
b
c
pos
Fig. 25: The position of a particle in 2D space
Fun with Freeze
RF_magazine
54
Scripting with RealFlow 02_2007
With this knowledge we can write our little script, imple-
menting the position check. The helper object, which is
used to define the boundary, is animated. With each step,
the new position is read out. To avoid troubles with
positive or negative position values, were simply taking
the absolute value abs(radius), while the Magnitude is
always positive. These two preconditions guarantee a
hassle-free simulation.
Events Script: CircularFreeze.rfs
def onSimulationBegin():
emitter = scene.getEmitter("Circle01")
emitter.setParameter("Speed", 2.0)
def onSimulationStep():
import math
emitter = scene.getEmitter("Circle01")
helper = scene.getObject("Helper")
h_pos_g = helper.getParameter("Position")
radius = h_pos_g.getX()
if (radius == 0.0):
emitter.setParameter("Speed", 0.0)
particle = emitter.getFirstParticle()
while (particle):
p_pos_g = particle.getPosition()
p_pos_x = p_pos_g.getX()
p_pos_z = p_pos_g.getZ()
p_mag = math.sqrt(p_pos_x ** 2 + p_pos_z ** 2)
if (p_mag >= abs(radius)):
particle.freeze()
particle = particle.getNextParticle()
The def onSimulationBegin( ) block is just for our conven-
ience, to reset the scene to its initial values. Something
youve already seen with the foam scripts is the import
statement. Here, were importing the math module, to
provide Python with functions, like sine, cosine or square
root.
To speed up the simulation, after the last particle has been
frozen, we prevent the emitter from spitting out further
particles. This condition is fulfilled when the helper object
has reached the centre of the scene:
if (radius == 0.0):
emitter.setParameter("Speed", 0.0)
The implementation of Pythagoras theorem can be found
here:
p_pos_x = p_pos_g.getX()
p_pos_z = p_pos_g.getZ()
p_mag = math.sqrt(p_pos_x ** 2 + p_pos_z ** 2)
As you can see from Fig. 26, the particles behave exactly as
predicted. Particles outside the circle shaped boundary are
darker, indicating lower speed.
Of course this script could be extended, to check against
elliptical, rectangular or other shapes.
Fig. 26: Particles outside the circle are frozen
Fun with Freeze
RF_magazine
55
Scripting with RealFlow 02_2007
Relaxing Particles with Freeze
Youve already learned a lot of things about the freeze
statement. The next question surely is, whether its
possible to use freeze for fast relaxation of particles? Each
time you want to generate a calm surface, you have to
wait for ages, until the particles have settled. The freeze
function seems to be a smart alternative to all known
methods, but theres one decisive difference:
With freeze you simply stop the motion of the particles at
a certain point. The particles energy will be preserved.
After unfreezing the particles again, the simulation goes
on as if nothing has happened (as long as all environmen-
tal conditions dont change). So, the freeze function could
be considered as an interruption of the current movement,
while all parameters and values remain the same.
The known relaxing methods really withdraw motion
energy from the particles over time. The particles settle
and the spaces between the water molecules become
smaller. The volume of the particle cloud decreases. With
an Initial State its possible to use settled particles for
further experiments.
The freeze method is only suited for simulations, where
you want to keep speed, impulse, and energy, or lock the
particles at a particular state.
Inverting the Effect
Until now weve only used Python to lock particles, but
this is just half the truth, because RealFlow also knows
the unfreeze method. The unfreeze function is great for
partial dissolving of objects or characters. By defining
boundaries, youre able to delete just portions of an
object or let it disappear using a Wind daemon. For this
purpose, again an animated helper object is needed.
These helpers are more than a gadget, they allow us to
have visible control over the settings.
The script itself needs no witchcraft, were just using the
known principles and techniques. The main part again
compares the current position of the helper object and
releases the particles, if the given condition is fulfilled.
In conjunction with an age function, the particles will
disappear after a certain time. Its not possible to apply an
Age daemon here, because its simply too static. After a
short while all particles would have been gone, even the
frozen ones. To avoid this, were introducing our own age
function.
The released particles (with a certain age) will be identi-
fied via their ID and then removed from the emitter.
Youve already met this principle with the foam script.
There, we shifted the particles to the foam emitter and
then removed them from the water source. Basically, this
is pretty much the same method.
The velocity of the helper object defines, how fast or slow
the particles will be released. This surely may take some
testing and adjusting, until you get the desired result.
Together with some daemons you can achieve impressive
effects and animations.
Fig. 27: Object controlled dissolving of a rocket primitive
Fun with Freeze
RF_magazine
56
Scripting with RealFlow 02_2007
Events Script: ObjectDissolve.rfs
def onSimulationBegin():
emitter = scene.getEmitter("Fill_Object01")
rocket = scene.getObject("Rocket01")
particle = emitter.getFirstParticle()
while (particle):
particle.freeze()
particle = particle.getNextParticle()
rocket.setParameter("Simulation", "Inactive")
def onSimulationStep():
import random
age_threshold = 0.5
emitter = scene.getEmitter("Fill_Object01")
helper = scene.getObject("Helper")
h_pos_g = helper.getParameter("Position")
h_pos_y = h_pos_g.getY()
particle = emitter.getFirstParticle()
while (particle):
rand = random.uniform(-0.2, 0.2)
p_pos_g = particle.getPosition()
p_pos_y = p_pos_g.getY()
age = particle.getAge()

if (p_pos_y > h_pos_y):
particle.unfreeze()
if (age >= age_threshold + rand):
id = particle.getId()
emitter.removeParticle(id)
particle = particle.getNextParticle()
The onSimulationBegin function initializes our scene,
including the freezing of all available particles from the Fill
Object emitter. The second step is to make the Rocket
inactive, to prevent the particles from colliding with the
object.
The unfreeze condition is a really simple if-clause, testing,
whether the particles are located above the helpers
y position (height) or not. Within this group of released
particles, the age check is performed. By altering the
age_threshold value you can increase/decrease the life
span easily. If you dont want the particles to disappear,
you can simply omit the age check!
Summary
The idea behind this chapter was to show you the possi-
bilities, you can get out from just two RealFlow functions.
Also all scripts are very short and use only basic maths.
With little efforts you can do stunning things. The only
precondition is a little time to make up your mind before
you start scripting.
Please remember that theres always more than one way
to solve a problem. The scripts, presented here are nothing
more than one possible solution. Maybe youll find a
shorter, better or faster method, but thats not the point
with this issue. The current edition of RF_magazine wants
to show you that you dont have to build sophisticated
and long-drawn-out scripts to achieve nice effects.
Fun with Freeze
RF_magazine
57
Scripting with RealFlow 02_2007
Overview
Possibilities with scripting are numerous and therere
solutions for many special issues and topics. To complete
the previous chapters, here are some programs, showing
you a variety of methods and principles, ready to use for
your own work.
Image Based Emitter Speed
The usage of images and bitmap gradients is a wide
spread technique for a dynamic control of parameters.
Fractal height fields are the most common example.
The greyscale or RGB intensity of a pixel is used to trans-
form this value into height information. More sophisti-
cated approaches use Displacement or Normal Maps to
affect geometry. Something similar can be found in
RealFlows manual on PDF page 171 08.2.Simulation
Tasks: Create A Customized Wave Pattern. There, an image
sequence is loaded into RealFlow. The gathered intensities
are transferred into vertex displacements to create an
animated wave.
Why shouldnt we use those values and intensities for
other purposes? This example teaches you, how to access
a TIFF image for controlling the speed of an emitter. The
script isnt just going through the image, reading out
some intensities. You can define a certain frame range, in
which the values will be applied to the emitter.
The image were using is just plain TIFF image: 10 x 300
pixels with a gradient, ranging from pure white to pure
black. Dont forget to store the images as RGB, not greys-
cale. The common notation for describing the size of an
image is width x height. Thats important, because well
receive a List of values, representing these directions.
In this example, were using the height value, but this is
just a matter of definition. The image format for this script
shouldnt be compressed, since artefacts may falsify the
values.
Events Script: ImageBasedSpeed.rfs
def onSimulationFrame():
import math
emitter = scene.getEmitter("Circle01")
start = 25
stop = 125
range = stop - start
frame = scene.getCurrentFrame()
root_path = scene.getRootPath()
image_path = str(root_path)+"/templates/SpeedGrad.tif"
template = Image.new()
template.open(image_path)
width = template.size[0]
height = template.size[1]

if ((frame >= start) and (frame <= stop)):
step_width = math.ceil(height / range)
step = (frame - start) * step_width
pixel_pos = template.getPixel(1, step)
intensity = pixel_pos[1]
speed = intensity / 100.00
emitter.setParameter("Speed", speed)
if (frame == stop):
emitter.setParameter("Speed", 0.0)
template.close()
The first part defines the needed Variables, including the
path, where the desired image has been stored. Its
important to differentiate between upper and lower cases.
The bitmap SpeedGrad.tif has been placed inside a direc-
tory of the project folder. We can get the project folder
easily be using scene.getRootPath( ). Wherever your project
is located, this statement will find it. After weve defined
the bitmap path, the image has to be opened inside
RealFlow.
Examples and Ideas
RF_magazine
58
Scripting with RealFlow 02_2007
The opening routine can be considered as a fixed construc-
tion. The script reads the image and creates a List for its
properties. The size statement gets the dimension. The
Data Type we get with size is an Integer. Using width and
height, defines the exact position of a pixel within a
bitmap. These positions are always Integers, because
theres no pixel address, like 1.25.

A 8-bit gradient from white to black delivers values
between 255 and 0. An emitter speed of, e.g. 193 is rather
strong, therefore we have to cut down the intensity. The
emitter.setParameter(Speed, value) expects a Float Data
Type, so we have to convert the intensity Integer into a
Float value. This is done by dividing the pixel strength
through 100.00. Using this expression
speed = intensity / 100
emitter.setParameter("Speed", speed)
would directly lead to an error:
Script error: Bad type argument for parameter Speed
The frame range has been introduced to adopt the avail-
able pixels to a certain simulation length. Of course, itd
be possible, to adjust the image size to the frame range.
Another new function is
math.ceil(step_width)
The ceil function is a member of the math module and
rounds up the result to the next higher Integer:
math.ceil(3.06) = 4
math.ceil(2.88) = 3
The opposite of math.ceil is math.floor.
The last statements are just for safety reasons and to free
memory: Its always a good idea to close an image, when
we dont need it anymore.
Boxed Daemons
This type of daemon is rather useful, because you can
specify a bounding object to affect the particles or bodies.
The clue with this script is that it uses a RealFlow primitive
to define the bounding box. The most obvious shapes are
cubes and spheres. The main task is to use relative coordi-
nates: We have to calculate the dimensions of the box,
dependent from its position in our 3D world. For simple
objects, this is not too difficult. The boundaries of a box
are given through the surfaces between the vertices. If we
know the position of the vertices, we also know the
dimensions. The Node panel offers all information we
need: Scale and Position.
We first calculate the dimension in x direction. Fortu-
nately, a box is symmetric object, and by halving the box,
we get the distance of the vertices from the centre:
dim_x_min = position_x + scale_x / 2
dim_x_max = position_x - scale_x / 2
Fig. 28: Example parameters of a daemons bounding box
Examples and Ideas
RF_magazine
59
Scripting with RealFlow 02_2007
The formula from page 58 can also be used for getting the
dimensions in y and z direction:
cube = scene.getObject("Cube01")

position_g = cube.getParameter("Position")
position_x = position_g.getX()
position_y = position_g.getY()
position_z = position_g.getZ()
size_g = cube.getParameter("Scale")
size_x = size_g.getX()
size_y = size_g.getY()
size_z = size_g.getZ()
dim_x_max = position_x + size_x / 2
dim_x_min = position_x - size_x / 2
dim_y_max = position_y + size_y / 2
dim_y_min = position_y - size_y / 2
dim_z_max = position_z + size_z / 2
dim_z_min = position_z - size_z / 2
The next step is to compare the dimension values against
the absolute position of the particles. For this purpose we
take the dim results and build new Vectors. This method is
much more reliable than just using the blank values, we
receive from the calculation above.
On the left you can see the results from our bounded
daemon. I used a freeze function to visualize the effect of
the script. The complete listing can be found here:
Examples_And_Ideas > BoxedDaemon > BoxedDaemon.rfs
Credits
The script is based on a program by A. Tena from Next
Limit and shows you methods, how to extend or modify
existing scripts. Its called Freeze Daemon and its avail-
able from the Next Limit scripting homepage:
http://www.nextlimit.com/nlscript/ranking_scripts.php?id=1
Exporting Position Data to AfterEffects
In some cases its necessary to use keyframed position
data with other applications. The RealFlow plug-ins are
designed for this purpose, but theres no possibility to
export these keys to other programs, e.g. AfterEffects.
If the desired application supports text files, then we have
an easy-to-use data format for exchanging RealFlow data.
In this example, were tracking the motion of a rigid body
object, moved by a daemons force. The position data will
be written as keys and exported into a text file simultane-
ously. Its not mandatory to write the keyframes within
RealFlow, but its a good opportunity to demonstrate this
function. Our first scene setup is pretty straightforward:
Just a Torus object, a Gravity Daemon and a Cube, serving
as a ground plane. The idea is to record the bouncing
motion of the Torus and transfer it to AfterEffects.
The first part creates a plain text file and writes out a
header for AfterEffects. Such a header is used by applica-
tions to recognize and initialize the data inside the file. The
next block generates keys for each frame and appends the
data to the text file. The last section closes our data file
and writes out a footer.
Examples and Ideas
RF_magazine
60
Scripting with RealFlow 02_2007
Writing Keys to the Curve Editor
Before were starting with text file operations, well have
a look at RealFlows class of instructions for the Curve
Editor. We want the position changes of the Torus object
stored as keys. This is called position baking. The advan-
tage with baked values is that they can be used many
times - even in other scenes. Were translating the auto-
matically calculated position data into a graphical repre-
sentation.
First, we have to specify the object, we want to use - here
its the Torus01 item from the Nodes window. Just by
calling the related curve, RealFlow adds a new curve to the
Curve Editor (so many curves...). The Key.new( ) statement
adds a new key to this curve. The last step is to add values
to the keys, in particular its Position and Time. Were
doing this for the y direction first:
def onSimulationFrame():
current_time = scene.getCurrentTime()
torus = scene.getObject("Torus01")
curve_y = torus.getParameterCurve("Position Y")
pos_g = torus.getParameter("Position")
pos_y = pos_g.getY()
# Add a new key only for the y position
newKey_y = Key.new()
newKey_y.time = current_time
newKey_y.value = pos_y
newKey_y.type = KEY_TYPE_TCB
curve_y.addKey(newKey_y)
We now have to repeat the key generation process for the
missing x and z values to get a full representation of the
movement. After the simulation has been performed,
well get a result similar to the one on the right.
Creating a Text File
Python provides several methods to handle text files. This
feature is one of Pythons fundamental functions. Plain
text files are often used with web applications for tempo-
rary or persistent storage. In RealFlow, text files can be
used to save and read values. To create a file, Python needs
to know several basic things:
Path and Directory
File name
Access type: read r , write w or append a
The keyword for the creation of a new file is open( ). Lets
say we want to store the file PositionData.txt into the
projects objects folder:
path = scene.getRootPath()
file = open(str(path)+"/objects/PositionData.txt", "r")
file.close()
By executing these lines of code, Python writes an empty
file into the scene folder. To fill the file, we also need some
content. For this purpose we use the write( ) statement.
The text, you want to print to the file has to be written
within the brackets, just like an argument:
path = scene.getRootPath()
file = open(str(path)+"/objects/PositionData.txt", "r")
file.write("My first text file")
file.close()
Examples and Ideas
RF_magazine
61
Scripting with RealFlow 02_2007
Now, weve got almost everything to write out the header
for our AfterEffects exchange file. Theres one thing left:
The so called Field Delimiter. This character separates fields
from each other, making them readable for other applica-
tions. In our case, the Field Delimiter is a tab character.
Python knows several fixed expressions for writing tabs,
blanks or carriage returns. For our script we need:
\t = tab
\n = new line
These combinations can be used directly within a text,
because Python searches through strings for expressions
like \t or \n. So the source code for the header is:
def onSimulationBegin():
path = scene.getRootPath()
file = open(str(path)+"/objects/PositionData.txt", "w")
file.write("Adobe After Effects 6.5 Keyframe Data\n\n")
file.write("\tUnits Per Second\t25.00\n")
file.write("\tSource Width\t100\n")
file.write("\tSource Height\t100\n")
file.write("\tSource Pixel Aspect Ratio\t1\n")
file.write("\tComp Pixel Aspect Ratio\t1\n\n")
file.write("\tFrame\tX pixels\tY pixels\tZ pixels\n")
file.write("Position\n")
file.close()
The header section shouldnt be modified, otherwise
AfterEffects may have serious trouble reading your keys.
This is the content directly from our freshly created file:
Writing Position Data to a Text File
Now we want to write the Position and Frame data to the
PositionData.txt file. Youve already learned how to treat
values and Variables within RealFlow scripts. This concept
is used again here, together with the write( ) statement.
Just one thing: We want to append the gathered values.
With the following expression, the file would be created
newly each frame and overwritten:
file = open(str(path)+"/AnimationData.txt", "w")
Instead of w, were just using a, for append:
file = open(str(path)+"/AnimationData.txt", "a")
The notation for the data part is written this way:
file.write("\t"+str(frame)+"\t"+str(pos_x)+"\t"+
str(pos_y)+"\t"+str(pos_z)+"\n")
The very last task is to print out a footer for AfterEffects.
This footer helps the application to recognize the end of
the data section. Again, weve got to open the file in
append mode:
def onSimulationEnd():
path = scene.getRootPath()
file = open(str(path)+"/AnimationData.txt", "a")
file.write("\n\n\nEnd of Keyframe Data")
file.close()
You can have a look at the entire listing here:
Examples_and_Ideas/Scripts/ExportPositionData_001.rfs
On page 62 youll see a screenshot from the file, gener-
ated with the current script. Therere still several issues,
we have to solve to write out an adequate file: The format
of the Float numbers is not suitable for AfterEffects.
Examples and Ideas
RF_magazine
62
Scripting with RealFlow 02_2007
The values of X pixels, Y pixels and Z pixels weve got here
cant be used within AfterEffects. We have to find a way
to format them. Also the values are way too small. Trans-
ferring the result to AfterEffects would result in a hardly
visible motion.
First, well cut down the pixel values. Python uses a special
notation for this purpose. A pretty reasonable result would
be to leave two digits after the decimal point. This can be
written as:
x_form = "%.2f" % pos_x
y_form = "%.2f" % pos_y
z_form = "%.2f" % pos_z
The most striking at this expression surely is the percent
character, which has already been introduced with the
Modulus operation. Here, the character has a completely
different meaning. The script takes the original pos_x
value and uses the complete value left from the decimal
point (%), and then the first two digits (2f) right from the
point. We can simply repeat this for pos_y and pos_z.
Please note that its not possible to calculate with these
formatted values. Operations have to be transformed
before the formatting process. The Float values are treated
mathematically correct and a rounding is performed, as
you can see on the right.
The output is almost perfectly suited for the export. For
printing these values, we of course have to use the new
Variables x_form, y_form and z_form:
file.write("\t"+str(frame)+"\t"+str(x_form)+"\t"+
str(y_form)+"\t"+str(z_form)+"\n")
You can find the complete script here:
Ideas_and_Examples/Scripts/ExportPositionData_002.rfs
By multiplying the original position values with 100, we
get useful results, suited for 3D scenes in AfterEffects.
But in many cases we only need the y value to get the
effect, were after. The last example extracts this value
and prints out a fixed x coordinate, while the z value will
be left blank. Were using a new scene with this script. The
Torus object will be replaced with a Sphere item.
The x coordinate is just the half of our composition size.
With NTSC 640 x 480 its 320, with HDTV 1280 x 720 its
640. Here, were using PAL and 384. For the y coordinate
we can write:
factor = 100
offset = 382
ae_pos_y = -pos_y * factor + offset
form_ae_pos_y = "%.2f" % ae_pos_y
Examples and Ideas
RF_magazine
63
Scripting with RealFlow 02_2007
The offset is empirical and helps us to find the right initial
position. Its up to you, where you want to set the starting
point. Also we have to use -pos_y, because we want the
object to fall downwards in AfterEffects. The next chapter
tells you, how to use the data within the program. You can
find the complete script here:
Ideas_and_Examples/Scripts/ExportPositionData_003.rfs
After Effects Specifications
The header is a bit different for each scene, you create
with AfterEffects. Just open a new composition and add
the element you want to use with the RealFlow data,
e.g. a Color Plane:
Composition > New Composition
Make your settings, like NTSC, PAL, HDTV or any other
format and specify the aspect ratios.
Layer > Color Plane
Make your settings
Add a position key to the Color Plane
Mark the key and copy it to the clipboard
Open a text program (WordPad, Word, TextEdit...) and
paste the clipboard content into a new text file
The text file now shows the header, directly from
AfterEffects. This file serves as a template, you have to
rebuild in RealFlow. The content should be similar to this
figure:
After the RealFlow file has been created, open it in a text
program and follow these instructions:
Copy the entire content from the file to the clipboard
Mark the desired object in AfterEffects
Paste the clipboard to the AE object
If therere any errors, AfterEffects prints out a message,
but if everything went right, you can immediately see the
keyframes and the motion curve in AfterEffects timeline:
Of course you can modify the position data in your text
editor or Microsoft Excel to your needs. The image below
shows the RealFlow position data, transferred to a pre-
rendered marble. The pink crosses indicate the keys, we
can also see in the timeline.
Examples and Ideas
RF_magazine
64
Scripting with RealFlow 02_2007
Reserved and Forbidden Words
Python uses some words and expressions internally and
they have a special meaning there. Thus its not allowed to
name Variables as:
and assert break class continue
def del elif else except
exec finally for from global
if import in is lambda
not or pass print raise
return try while
These names should be avoided, though they arent
strictly forbidden, but may cause conflicts:
array close data float int
input numeric open range type
write zeros
Maths related expressions should not be used. You must
avoid them when the math library is imported:
acos asin atan cos e
exp fabs floor log log10
pi sin sqrt tan
Also all keywords from RealFlow are forbidden. You can
simply identify the RealFlow command, as they turn
yellow, after theyve been typed into one of the scripting
windows. A complete overview is available from the online
helps scripting reference.
Appendix
RF_magazine
65
Issue 1_2008: Rigid Body Dynamics
The scheduled release date of Issue 1_2008 is January, 28th 2008
For updates and contents, please visit http://www.rf-magazine.com
Please note:
The topic may be subject to change. Further information and contents will be available from
http://www.rf-magazine.com
Legal Notes
All materials, texts, files and images are copyrighted to Liquidlight.tv, Thomas Schlick. Its forbidden to copy, share or provide
the materials as a whole or as excerpts. The materials must not be shared in forums, file sharing or torrent servers or similar
facilities. All images, texts and materials are property of Liquidlight.tv, Thomas Schlick and must not be copied or modified.
The magazine and its materials must not be copied and given to third parties, neither free of charge nor against payment.
The publication of citations, images, files or text excerpts is only allowed with written permission from Liquidlight.tv, Thomas
Schlick. All violations of these terms will be prosecuted.
Notes 02_2007
RF_magazine
Copyright Liquidlight.tv 2007
For updates, news and more please visit http://www.rf-magazine.com

You might also like