Writing Moov Simulation Scripts 

 

Overview 

 

Structure and hooks 

The Moov simulation script is interpreted by the built-in Python 2.7 interpreter and should be correct Python code. Errors from the Python interpreter will be printed out to the script console.

In addition to being correctly formed Python, the script is expected to contain the following names:

  • MoovDefaultParameters: tuple of tuples containing the descriptions and default values for the script's input parameters that will be exposed through the host's user interface ("Moov Simulation Parameters" in Maya's Attribute Editor, "Script Parameters" rollout in 3ds Max, "User Data" parameters in C4D). For a detailed description see User Interface.
  • Evaluate: main hook; this function is invoked when the animation time changes or when any of the parameters is changed from the UI. More details here: Evaluation function.
  • _solver: (optional) the global Moov solver object. If it doesn't exist in the script, it will be created behind the scenes. More details: Moov solver.
 

Simple example 

Here is the simplest possible example script that contains only the bare minimum necessary to run: the MoovDefaultParameters tuple (empty in this case) and the Evaluate function, set up to print the strand count on each evaluation.

# Parameter descriptions and defaults
MoovDefaultParameters = ()

# Main hook
def Evaluate( hair, time, resetSolver, resetHairModel, newInputParamsDict, initialStateCapture ):
    print hair.GetStrandCount()
    return True
 

Parameters and user interface 

The simulation script can define any number of parameters that are exposed to the user interface. These parameters are described by the MoovDefaultParameters global variable. It is a tuple where each element describes a parameter in the form (name: string, type: string [, additional_values]):
  • name is a string representing the unique name of the parameter. Parameter names have to be unique, two parameters with the same name will trigger an error. The parameter name is used in the corresponding GUI control and also to access the current value of this parameter from the newInputParamsDict passed to the Evaluate function.
  • type is a string describing the type of the parameter. Possible values are 'int', 'float', 'bool', 'string', 'time', 'enum', 'ChannelSelector', 'RampCurve', and 'DataGenerator'. There are also two additional types 'Group' and 'Separator' that help to format the user interface.
  • additional_values are one or more additional values used to initialize the parameter. Their number and type depends on the type of the parameter; some types don't require additional values. Most parameters require at least one additional value giving the default value of the parameter, and some allow optional values defining the numerical range of the UI slider controls.

The parameter values stored in the MoovDefaultParameters variable specify only the initial (default) values that are used when building the user interface for a new MoovPhysics operator. The current values of the parameters can be read from the dictionary newInputParamsDict passed to the Evaluate function. The parameters are read-only; their values cannot be set from the Python script (except the defaults).

 

Parameter types 

 

int 

Holds an integer value. The parameter description is (name: string, 'int', default: int [, min: int, max: int]). Specifying a default value is mandatory, minimum and maximum values are optional.

 

float 

Holds a floating point value. The parameter description is (name: string, 'float', default: float [, min: float, max: float]). Specifying a default value is mandatory. Minimum and maximum values are optional.

 

bool 

Holds a boolean value (True or False). The parameter description is (name: string, 'bool', default: bool). Specifying a default value is mandatory.

 

string 

Holds a string value. The parameter description is (name: string, 'string', default: string). Specifying a default value is mandatory.

 

time 

Holds a time value (floating point) in seconds. The parameter description is (name: string, 'time', default: float). Specifying a default value is mandatory.

The difference from an ordinary floating point value is that it can be displayed in different units (i.e. frames) that can be set up by the user.

 

enum 

Holds an integer value representing the index of a selected item in a drop-down box. The parameter description is (name: string, 'enum', default: int, enumName1: string, ...). Specifying a default value is mandatory. The remaining string values define the names of all items displayed in the drop-down box.

 

ChannelSelector 

Holds an integer value, representing an encoded data channel index as selected by the user. The parameter description is (name: string, 'ChannelSelector'). The user interface displays a drop-down box which is automatically filled in with the available data channels.

The channel indices are encoded as follows:
  • 0 means no channel is selected;
  • 1-1001 correspond to per-strand channels 0-1000;
  • values larger than 1001 correspond to per-vertex channels with index (1001 - channelSelectorValue).

Some useful utilities for working with data channels (including index decoding and access to channel data) can be found in the class HairModel.StrandChannel.

 

RampCurve 

Holds an IFunction1 object, representing a function with one parameter that allows to obtain ramp values (see the documentation for the ephere_ornatrix Python module). The parameter description is (name: string, 'RampCurve' [, control_point_values]). Each control point of the ramp is described by two floating point and one integer value: xPosition: float, yPosition: float, interpolationType: int. The xPosition and yPosition values should be between 0 and 1. The allowed interpolation types are:
  • 0: no interpolation;
  • 1: linear interpolation;
  • 2: smooth interpolation;
  • 3: spline interpolation.

For example, let us take the following parameter definition:

MoovDefaultParameters = (
    ( 'ExampleRamp', 'RampCurve',
        0.0, 1.0, 3,     # First point: x = 0, y = 1, interpolation 3 (spline)
        0.3, 0.5, 3,     # Second point: x = 0.3, y = 0.5, interpolation 3 (spline)
        1.0, 0.0, 3,     # Third point: x = 1, y = 0, interpolation 3 (spline)
    ),
)

This produces the following ramp-down curve:

Specifying the control points is optional; if absent, the ramp curve will be initialized with a default ramp-down curve.

 

DataGenerator 

Holds a MoovDataGenerator object, allowing the evaluation of geometric queries on user-selected scene objects. The parameter description is (name: string, 'DataGenerator'). The user interface displays a list box with scene objects and buttons for adding and removing objects from the list. For more information on how to use this parameter see the documentation for the ephere_ornatrix Python module.

 

Separator 

Creates a vertical separator between parameters. Used only for formatting the user interface. The parameter description is (name: string, 'Separator').

 

Group 

Creates a group of parameters. Used only for formatting the user interface. The parameter description is (name: string, 'Group', collapsedByDefault: bool). The boolean parameter specifies if the group will be collapsed (True) or expanded (False) by default.

 

Evaluation function 

The Evaluate function is called when the MoovPhysics operator is evaluated. This happens when the current time of the scene changes, when the hair object passed to the operator changes, or when a parameter value is changed by the user. The function's signature is

def Evaluate( moovHairSimulator, time, resetSolver, resetHairModel, newInputParamsDict, initialStateCapture ):
    # Do business
    return True # if everything is fine, else return False
 

Parameters 

  • moovHairSimulator: MoovHairSimulator - a MoovHairSimulator object that is updated by the caller with current data on the hair, collision meshes, and force fields specified in the MoovPhysics interface. The underlying IHair object can be retrieved using moovHairSimulator.GetHair(). For more information see the documentation of the ephere_ornatrix Python module.
  • time: float - current scene time in seconds.
  • resetSolver: bool - indicates if the Moov solver needs to be reset.
  • resetHairModel: bool - indicates if the Python hair model needs to be reset (e.g. if the underlying hair geometry has changed).
  • newInputParamsDict: dict - dictionary containing the current values of all parameters, with parameter names as keys.
  • initialStateCapture: StateCapture - a Moov state capture object containing the initial state of the simulation. For more information see the documentation of the ephere_moov Python module.
 

Return value 

  • True if evaluation proceeds as planned; False if there was an error during evaluation.
 

Accessing the GUI parameters 

The values of the GUI parameters are passed to Evaluate in the dictionary newInputParamsDict, and can be accessed using the parameter names as keys.

 

Simple-valued parameters 

For simple parameter types like int, float, bool, string, the dictionary contains the corresponding value. Parameters of type time contain a floating-point time value in seconds. enum parameters contain an integer index of the selected item.

Here is an example script demonstrating the usage of simple-valued parameters:

# Immutable tuples defining the GUI parameters
MoovDefaultParameters = (
               ( 'IntParam', 'int', 10, 1, 20 ),
               ( 'FloatParam', 'float', 10000.0, 0.1, 1000000000.0 ),
               ( 'StringParam', 'string', '' ),
               ( 'EnumParam', 'enum', 0, 'Choice0', 'Choice1', 'Choice2' ),
               )

def Evaluate( moovHairSimulator, time, resetSolver, resetHairModel, newInputParamsDict, initialStateCapture ):

    print "parameter value for 'IntParam': {}".format( newInputParamsDict['IntParam'] )
    print "parameter value for 'FloatParam': {}".format( newInputParamsDict['FloatParam'] )
    print "parameter value for 'StringParam': {}".format( newInputParamsDict['StringParam'] )
    print "parameter value for 'EnumParam': {}".format( newInputParamsDict['EnumParam'] )
    return True

 

RampCurve 

The value of a RampCurve parameter is an IFunction1 object. The y-values of the ramp can be obtained using the IFunction1.Evaluate function. Here is an example of its usage that prints the ramp values for x-values at 0.1 intervals:

# Immutable tuples defining the GUI parameters
MoovDefaultParameters = (
               ( 'CurveParam', 'RampCurve' ), # initialized with a default ramp-down curve
               )

def Evaluate( moovHairSimulator, time, resetSolver, resetHairModel, newInputParamsDict, initialStateCapture ):

    curve = newInputParamsDict['CurveParam']

    # Print curve values for x = [0..1]:
    xValues = [ float( i )/10.0 for i in range( 11 ) ]

    for x in xValues:
        print "ramp curve value at {} is {}".format( x, curve.Evaluate( x ) )

    return True

 

DataGenerator 

The value of this parameter is a DataGenerator object. It provides the following interface:
  • Evaluate(pointsList: list, method: DataGenerationMethod) - allows evaluating a geometric function on a list of points against the scene objects set in the user interface. The function returns a list of floating-point values (one or several values per each point), depending on the chosen method. The available data generation methods are:
    • DataGenerationMethod.IsInsideAny: generates a single value of 0 or 1 per point based on whether the point is inside one of the specified scene shapes.
    • DataGenerationMethod.IsInsideEach: generates a list of 0/1 values based on whether each point is inside each of the specified scene shapes.
    • DataGenerationMethod.IsInsideDistanceAndNormal: generates a set of five values per point, containing:
      • one 0/1 value indicating if point is inside a shape;
      • one value giving the closest distance of the point to the shape;
      • three values giving the X, Y, and Z components of the normal to the nearest polygon.
  • GetObjectCount() - returns the number of user-specified scene objects currently assigned to this data generator.

Here is an example of the usage:

import ephere_ornatrix as ox

# Immutable tuples defining the GUI parameters
MoovDefaultParameters = (
               ( 'SampleDataGenerator', 'DataGenerator' ),
               )

def Evaluate( moovHairSimulator, time, resetSolver, resetHairModel, newInputParamsDict, initialStateCapture ):

    dataGenerator = newInputParamsDict['SampleDataGenerator']

    points = [ [3, 3, 3], [0, 0, 0] ]

    # A simple example:

    # The 'IsInsideAny' data generation method returns a single value per point:
    #   1 if the point is inside any of the scene objects
    #   0 if the point is outside all scene objects
    generatedData = dataGenerator.Evaluate( points, ox.DataGenerationMethod.IsInsideAny )

    for point, value in zip( points, generatedData ):
        print "point {} value is: {}".format( point, value )

    # A slightly more complex example:

    # The 'IsInsideEach' data generation method returns as many values per point as there are scene objects.

    objectCount = dataGenerator.GetObjectCount()
    generatedData = dataGenerator.Evaluate( points, ox.DataGenerationMethod.IsInsideEach )

    for pointIndex in range( len( points ) ):
        print "point {}: {}".format( pointIndex, points[pointIndex] )
        for objectIndex in range( objectCount ):
            generatedValue = generatedData[pointIndex * objectCount + objectIndex]
            state = 'inside' if generatedValue > 0.5 else 'outside'
            print "       is {} object {}".format( state, objectIndex )

    return True

 

Embedded Python modules 

The Ornatrix plugin contains two embedded Python modules: ephere_moov for access to the Moov simulation engine; and ephere_ornatrix for access to Ornatrix hair and scene objects. These modules are accessible for import when the Ornatrix plugin is loaded.

 

Utility Python modules 

The following Python modules are used by the default Moov hair simulator script and can be found in the Ornatrix script directory:
  • HairModel: utility classes for creating and manipulating a particle model for hair using the Moov simulation engine.
  • SolverUpdater: utility classes for updating the Moov simulation with root positions and external forces at each simulation step. Contains both OpenCL and non-OpenCL implementations.
  • Math: contains utility classes implementing vector, matrix, and quaternion math functions.
Missing Something? Let us know if this page needs more information about the topic.