Falling Pencils

In real life, even identical objects have slight variations in mass, friction, elasticity etc. These differences are often essential for a realistic simulation. But, changing the physical attributes for dozens or hundreds of objects manually is an unrewarding task. This tutorial shows you how to do this with a basic script from the RealFlow manual's "Python - An Introduction" section.


The result of the workshop. Rendering has been made with Maxwell Render.


Here is the nodes list:

  • 40 – 50 identical objects, e.g. pencils
  • 1 “Cube” object
  • 1 “Gravity” daemon

All scene elements can be added from RealFlow's “Edit” menu:

  • Add > Objects | Emitters | Daemons

  • RealFlow nodes can be moved, scaled, and rotated with the W, R, and E keys.

  • Imported objects from SD files have to be unlocked before they can be transformed with Selected object > Node Params > Node > SD ↔ Curve

  • Viewport perspective is changed with the 1, 2, 3, and 4 keys.

  • Shading modes are toggled with the 7, 8, 9, and 0 keys.

The Setup

The pencils' positions and rotations have been prepared in a 3D program, and then exported using the connectivity plugins' “SD Exporter” tool. This helper converts the objects, so they can be used with RealFlow. Instead of RealFlow's native SD format, the objects can also be stored as an Alembic file. Alembic (ABC) is a common, platform-independent format and supported by all major 3D applications. Please mind the scale when you work with Alembic files; the exporter plugin will do this conversion automatically.

  • Ctrl + I > load the SD or ABC file with the objects
  • Select the imported objects > right-click on one of them > Group
  • Group node > right-click > Rename > Pencils
  • Create ground object with Edit > Add > Objects > Cube
  • Rescale the cube node with the R key, and limit its height to a value around 0.1 under Node Params > Node > Scale
  • The “Cube01” node should be a static object – just like a ground node: activate the object's RBD feature under Node Params > Node > Simulation > Passive rigid body
  • Add a “Gravity” daemon from Edit > Add > Daemons

 

 

If you are not happy with the pencils please go back to your 3D program (do not close RealFlow):

  • Adjust the objects' positions, and export everything again. Just overwrite the existing file.
  • In RealFlow, press Ctrl/Cmd + U to update the SD file.

Preparing the Script

This is the tutorial's most important part and you will learn how to use and extend an existing Python script. The idea is to enable the pencil’s “Active rigid body” feature, because they are meant to move and fall onto the “Cube01” ground object. When an object's RBD attribute is active RealFlow will calculate a mass value. Since all nodes are identical here, they will share exactly the same mass value. “@ object friction” and “@ elasticity” are both set to 0.3 by default.

Currently you will not be able to locate these values, because the pencil’s “Active rigid body” mode is not enabled.

The task is to randomize these values automatically with the help of a so-called batch script. This script type is perfectly suited to complete repetitive tasks:

  • Open Python Examples and Ideas > Random Mass Change
  • If you are interested in the script's details and how it works please read the page.
  • Copy the script at the end of the page (“So the entire script looks like this...”).
  • Go to RealFlow and open a “Batch Script” editor from the “Layout” menu.
  • Paste the clipboard to the editor.
  • From the editor's menu choose Script > Syntax check. The script should not contain any errors.

 

Do not run the script, because by now only the pencils' mass value will be changed, but we also want to randomize object friction, elasticity, and – this is new – the bodies initial velocity. The latter step avoids the pencils moving at the exact same speed. Mass is already processed by the default script, but the remaining parameters are treated similarly. For every parameter a random value is required. Let's say:

  • “@ object friction” should range between 0.15 and 0.25

  • “@ elasticity” should be between 0.9 and 1.0

  • “@ Velocity” should be between -2.0 and -1.0 to make them fall

The corresponding commands are:

  • randomFriction   = random.uniform(0.15, 0.25)

  • randomElasticity = random.uniform(0.9, 1.0)

  • randomVelocity   = random.uniform(-2.0, -1.0)

 

The result from this operation is simply used to specify the node's properties. The appropriate command is just: node.setParameter(“parameter name”, parameter value). The final notation is:

  • node.setParameter("@ object friction", randomFriction)

  • node.setParameter("@ elasticity", randomElasticity)

 

When you take a look at “@ Velocity” you will see that it contains three values – it is a so-called vector. The pencils should fall, and therefore only the vertical component is required. Since most 3D programs work with different axis systems you have to know which axes serves as the height (= vertical) axes. Then it is possible to assemble the complete vector. Of course, only one of the commands is required:

  • Y setup: newVelocity = Vector.new(0.0, randomVelocity, 0.0)

  • Z setup: newVelocity = Vector.new(0.0, 0.0, randomVelocity)

 

Now, the new value can be set following the same principle as before:

  • node.setParameter("@ Velocity", newVelocity)

 

Example values after running the script below.


Add the new lines to the existing script. Please mind the indents and use the Tab key to create them, because otherwise you will get syntax errors! You can also copy and paste the script below to the “Batch Script” editor to be on the safe side. Your final program should look like this – here is the script for the Y setup:

 

from time import *
import random

startTime         = clock()
percentVariation  = 10
userSelection     = scene.getSelectedNodes()

for node in userSelection:
    rbdState = node.getParameter("Dynamics")

    if (rbdState != "Active rigid body"):
        node.setParameter("Dynamics", "Active rigid body")		

    currentMass      = node.getParameter("@ mass")
    rangeMass        = (currentMass / 100) * (percentVariation / 2)
    randomMass       = random.uniform(-rangeMass, rangeMass)
    randomFriction   = random.uniform(0.15, 0.25)
    randomElasticity = random.uniform(0.9, 1.0)
    randomVelocity   = random.uniform(-2.0, -1.0)
    newMass          = currentMass + randomMass
    newVelocity      = Vector.new(0, randomVelocity, 0) # Y setup

    node.setParameter("@ mass", newMass)
    node.setParameter("@ object friction", randomFriction)
    node.setParameter("@ elasticity", randomElasticity)
    node.setParameter("@ Velocity", newVelocity)

endTime  = clock()
diffTime = endTime - startTime

scene.message("\nProcess finished...\nElapsed time: "+str(diffTime)+" seconds")

Running the Script and the Simulation

With just a few lines of code and an existing script we have automatized the complete task of randomizing dozens of objects. Imagine how long it would have taken to do this manually. If you want to change other parameters, e.g. “@ air friction”, you just have to follow the instructions before.

  • Select all pencil objects – and only the pencils from the “Nodes” panel.

  • Go to the “Batch Script” editor's menu and choose Script > Run

  • Wait until you can see a note in the “Messages” window similar to this:
    Process finished...
    Elapsed time: 7.544235 seconds

  • Check the values.

  • Simulate.

 

A frame from the final simulation. Every pencil has different rigid body properties.

 

Maybe your pencils are moving too far, leaving the ground object. In this case you have two simple options:

  • Cube01 > Node > Scale > increase the horizontal values (= make it bigger)

  • Cube01 > Rigid Body > @ object friction > 0.7