Writing Plugin Brushes
In Edit Guides modifier you can use plugin brushes to brush hair in any user-defined manner. This provides unlimited possibilities to hair editing via our brushing interface by allowing you to quickly and easily prototype and develop custom brush tools. The brushes are written in VFX-friendly Python language and saved as .py files into our common "User Brush" directory. Ornatrix will automatically discover user brush scripts and make them available for selection via the Attribute Editor.
User brush directory
The plugin brush files are located in "scripts/Brushes" in the Ornatrix installation directory. The standard locations are:- Windows: "C:\Program Files\Ephere\Ornatrix for Maya\scripts\Brushes".
- Linux: "/opt/Ephere/Plugins/Autodesk/Maya/Ornatrix/scripts/Brushes".
Brush scripts are read once when Ornatrix is loaded. To refresh the brush list with newly added brushes and/or reload modified scripts, press the "Reload Brushes" button in Attribute Editor. All "*.py" files from the user brush directory are read; error and diagnostic messages are printed in the Maya Script Editor window.
Writing a brush Python file
Structure and hooks
The brush script contains standard Python code that is executed by the Maya Python interpreter. The Ornatrix functionality can be accessed by importing the Ornatrix Python module.
The entire brush script is executed once when it is loaded. After that, hooks are executed on each brush stroke.
Evaluate
def Evaluate( guidesEditor, brushParameters, brushDirectionInObjectSpace, brushStrokeLength, strandIndices )
An Evaluate
function with the above signature must be defined in the brush file and is called on each brush stroke. It receives the following parameters for processing:
guidesEditor
- anIGuidesEditor
objectbrushParameters
- aBrushParameters
objectbrushDirectionInObjectSpace
- aVector3
vector giving the direction of the brush strokebrushStrokeLength
- float giving the length of the brush strokestrandIndices
- a list of integer strand indices affected by the brush stroke
PreCompute and PostCompute
TODO
Brush name
A BrushName
string can be defined in the script to identify this brush in the list of user-defined brushes. If this is not defined, the file name is used.
Ornatrix Python Module
The embedded Ornatrix Python module (from Ornatrix plugin file) provides access to Ornatrix functionality for the brush script, including functions for querying and editing the hair object, access to brush parameters, etc. The module name is ephere_ornatrix
, and it can be imported e.g. as:
import ephere_ornatrix as ox
This module can only be used when the Python interpreter is started by Ornatrix.
Vectors
The ox.Vector3
class provides common vector operators and functions such as addition, subtraction, dot product (Dot
), squared length (LengthSquared
), etc.
Coordinate space
The ox.CoordinateSpace
enum is used to indicate the coordinate space for hair vertex coordinates. The allowed values are ox.CoordinateSpace.Strand
(strand space) and ox.CoordinateSpace.Object
(hair object space).
Guides editor
TheIGuidesEditor
object is passed as first parameter to Evaluate
and has the following methods:
GetEditableGuides()
- returns a hair interface,IHair
, that can be used to manipulate the current hair object.GetVertexWeights(strandIndex)
- returns a list of weights for the vertices of the correspondingstrandIndex
during the current brush stroke. Vertex weights vary from 0 (vertex not affected) to 1 (vertex fully affected) and reflect fall-off from the brush center.
Hair interface
The hair interfaceIHair
provides the following methods for querying and modifying a hair object:
GetStrandPoints(strandIndex, coordinateSpace)
- returns list ofVector3
coordinates of strand vertices.SetStrandPoints(strandIndex, pointsList, coordinateSpace)
- sets strand vertex coordinates to theVector3
values frompointsList
In the above functions, coordinateSpace
is an ox.CoordinateSpace
enum value.
GetChannelIndex(strandDataType, channelName)
GetStrandChannelCount(strandDataType)
SetStrandChannelCount(strandDataType, count)
GetStrandChannelData(channelIndex, strandIndex)
GetStrandChannelData(channelIndex, strandIndex, pointIndex)
GetVertexChannelData(channelIndex, vertexIndex)
SetStrandChannelData(channelIndex, strandIndex, floatValue)
SetStrandChannelData(channelIndex, strandIndex, pointIndex, floatValue)
GetUniqueStrandChannelName(strandDataType, rootName)
GetStrandChannelName(strandDataType, channelIndex)
SetStrandChannelName(strandDataType, channelIndex, name)
Brush options
The BrushParameters
object provides read-only access to brush options set in the Attribute Editor. The currently implemented parameters are:
GetStrength(usePenPressure = True)
- retrieves the Brush Strength(float)
affectSelectedOnly
(bool)
affectByRootsOnly
(bool)
respectVertexWeights
(bool)
preserveStrandLength
(bool)
optimizeStrandGeometry
(bool)
resampleStrands
(bool)
A quick example
# Import embedded Ornatrix module
import ephere_ornatrix as ox
# User-defined brush name, shown in the drop-down list for brush selection. If not defined, the file name will be used
BrushName = 'Demo Move'
def Evaluate( guidesEditor, brushParameters, brushDirectionInObjectSpace, brushStrokeLength, strandIndices ):
"""Applies a brush stroke to a list of strands."""
hair = guidesEditor.GetEditableGuides()
brushStrength = brushParameters.GetStrength() * brushStrokeLength
for strandIndex in strandIndices:
# Multiply strength by soft selection if only affecting selected strands
strength = brushStrength if not brushParameters.affSelOnly else brushStrength * hair.GetStrandChannelData( 0, strandIndex )
delta = brushDirectionInObjectSpace * strength
strandPoints = hair.GetStrandPoints( strandIndex, ox.CoordinateSpace.Object )
vertexWeights = guidesEditor.GetVertexWeights( strandIndex )
# Start from 1 to avoid modifying the root vertex
for pointIndex in range( 1, len( strandPoints ) ):
strandPoints[pointIndex] += delta * vertexWeights[pointIndex]
hair.SetStrandPoints( strandIndex, strandPoints, ox.CoordinateSpace.Object )