How To - Practical Examples >

# How To ... Create Scripted Geometry Plug-in

How To > Create Scripted Geometry Plug-in

MAXScript lets you create scripted geometry plug-ins which can generate procedural mesh objects on the fly just like regular C++plug-ins.

We are going to use the simple mesh code from the How To ... Create Custom Mesh Object topic and extend it to a scripted plug-in.

Related Topics:

Defining SimpleObject plug-ins

Scripted mouse tools

Mouse tool clauses

NATURAL LANGUAGE
Define a simpleObject scripted plug-in
Convert the basic parameters of the mesh object created in the first tutorial to parameter block parameters.
Define a UI and link to the parameter block values
Paste the original mesh code into the buildMesh handler of the plug-in
Create a mouse tool to place the object and define the parameters

MAXSCRIPT
    plugin simpleObject AntiStar
    name:"AntiStar"
    category:"HowTo"
    classID:#(0xe855567c, 0xbcd73b8b)
    (
    parameters main rollout:params
    (
    num_faces type:#integer ui:num_faces default:10
    radius1 type:#float ui:radius1 default:0
    radius2 type:#float ui:radius2 default:0
    width type:#float ui:width default:1
    )
    rollout params "AntiStar"
    (
    spinner num_faces"# Faces"range:[2,100,10] type:#integer
    spinner radius1"Radius 1 "range:[0,10000,0]
    spinner radius2"Radius 2 "range:[0,10000,0]
    spinner width "Width "range:[0,90,1]
    )

    on buildMesh do
    (
    --Note: The code below has been taken from the
    --How To – Create Custom Mesh Object --topic and is identical.

    vert_array = #()
    face_array = #()
    vert_count = 0
    for a = 0 to 360 by 360.0/num_faces do
    (
    v1 = [radius1*cos(a+width),radius1*sin(a+width),0]
    v2 = [radius1*cos(a-width),radius1*sin(a-width),0]
    v3 = [radius2*cos(a),radius2*sin(a),0]
    append vert_array v1
    append vert_array v2
    append vert_array v3

    append face_array [vert_count+1,vert_count+3,vert_count+2]
    vert_count += 3
    )
    setMesh mesh verts:vert_array faces:face_array
    )--end buildMesh
    tool create
    (
    on mousePoint click do
    (
    case click of
    (
    1: coordsys grid (nodeTM.translation = gridPoint)
    )
    )
    on mouseMove click do
    (
    case click of
    (
    2: (radius1 = abs(gridDist.y))
    3: (radius2 = abs(gridDist.y))
    4: (width = abs(gridDist.x))
    5: (#stop)
    )
    )
    )--end create
    )--end plugin

## Step-By-Step

    plugin simpleObject AntiStar
    name:"AntiStar"
    category:"HowTo"
    classID:#(0xe855567c, 0xbcd73b8b)
    (

A scripted plug-in starts with the constructor plugin followed by the superclass of the scripted plug-in ( simpleObject ) and the new class name of the plug-in ( AntiStar ). In addition, we have to provide the name which will appear in the effects list, and a unique classID .

The classID is used by 3ds Max to recognize the plug-in class when loading the scene. To generate a new unique ID, you can use the GenclassID () method in the Listener and copy the result into the script.

To keep all versions of this plug-in compatible around the world, it is recommended to copy the above classID though.

Scripted SimpleObject Plug-ins

classID

    parameters main rollout:params
    (

The scripted plug-in will store its parameters in a so-called Parameter Block. The ParamBlock2 format has been introduced in 3ds Max 3 and allows for easy properties and tracks management, automatic animation brackets in the UI and much more. In our case, we will store the same values used in the first mesh script (number of faces, the two radii and the width (offset) value in the ParamBlock.

All parameters in the block will have their counterparts in the plug-ins rollout in the UI. By providing the rollout name, we allow the ParamBlock to establish an automatic connection between the internal values and the UI elements.

Mid Topic ID: Parameter_block2

    num_faces type:#integer ui:num_faces default:10

The num_faces object property will control the number of "rays" in the object, actually the number of faces. It will be an integer and will correspond to a spinner in the UI with the same name. The default number of faces will be 10.

    radius1 type:#float ui:radius1 default:10

The radius1 object property will control the outer radius of the object. It will be a floating point value and will correspond to a spinner in the UI with the same name. The default radius on creation will be 10. If you create an object via MAXScript by calling the constructor antiStar(), the object will use this value as the default outer radius.

    radius2 type:#float ui:radius2 default:0

The radius2 object property will control the inner radius of the object. It will be a floating point value and will correspond to a spinner in the UI with the same name. The default radius on creation will be 0.

    width type:#float ui:width default:1

The width object property will control the offset of the outer vertices and will define the width of the rays. It will be a floating point value and will correspond to a spinner in the UI with the same name. The default width on creation will be 1.

    )
    rollout params "AntiStar"
    (
    spinner num_faces "# Faces" range:[2,100,10] type:#integer
    spinner radius1 "Radius 1" range:[0,10000,0]
    spinner radius2 "Radius 2" range:[0,10000,0]
    spinner width "Width" range:[0,90,1]
    )

Now we provide the rollout with UI elements referenced by the Parameter Block. The UI elements can limit the input values using the optional range: parameter, but the default value will be taken from the ParamBlock2.

Rollout Clauses

Spinner

    on buildMesh do (

The buildMesh is the main handler of scripted simpleObject plug-ins. It is called each time the geometry needs to be updated. Inside the handler, we can use our existing mesh generation code from the other tutorial to generate the vertices and faces of the mesh.

    vert_array = #()
    face_array = #()
    vert_count = 0
    for a = 0 to 360 by 360.0/num_faces do
    (
    v1 = [radius1*cos(a+width),radius1*sin(a+width),0]
    v2 = [radius1*cos(a-width),radius1*sin(a-width),0]
    v3 = [radius2*cos(a),radius2*sin(a),0]
    append vert_array v1
    append vert_array v2
    append vert_array v3
    append face_array [vert_count+1,vert_count+3,vert_count+2]
    vert_count += 3
    )
    setMesh mesh verts:vert_array faces:face_array

The buildMesh handler accepts the setMesh method which feeds the mesh generation data into a fast routine which regenerates the object on the fly.

    )--end buildMesh
    tool create
    (

The tool create handles the mouse controls on object creation.

Scripted Mouse Tools

    on mousePoint click do
    (

The on mousePoint handler is executed each time the user clicks with the mouse in the viewport. The variable click will count the number of clicks.

Mouse_Tools_Event_Handlers

    case click of
    (
    1: coordsys grid (nodeTM.translation = gridPoint)

In case the user has clicked for the first time, we can set the translation part of the Node Transformation Matrix of the object to the grid point the user specified. This repositions the center of the object to the clicked point in the active grid.

Mouse_Tools_nodeTM

Mouse_Tools_gridPoint

    )
    )
    on mouseMove click do
    (

The on mouseMove handler is executed each time the user moves the mouse in the viewport. The variable click will count the number of clicks so far.

Mouse_Tools_Event_Handlers

    Case click of
    (

The case expression allows the selection of options based on the value of the supplied variable. It is similar to using multiple if statements to compare the same variable with multiple values.

Case Expression

    2: (radius1 = abs(gridDist.y))

If the user has clicked twice so far, set the outer radius to the Y distance from the center of the object to the current mouse position.

Mouse_Tools_gridDist

    3: (radius2 = abs(gridDist.y))

If the user has clicked tree times, set the inner radius to the Y distance from the center of the object to the current mouse position.

    4: (width = abs(gridDist.x))

If the user has clicked four times already, set the offset if the outer vertices by measuring the X distance from the center of the object to the current mouse position.

    5: (#stop)

Finally, finish the creation. A new object has been born!

    )
    )
    )--end create
    )--end plugin

## Using the Script

This is a scripted plug-in and it will be handled by 3ds Max as a real C++ plug-in.

To install it permanently on your system, just copy the file to the \plugins folder or to any folder listed in the plug-ins paths.

To try the plug-in without installing it permanently, just evaluate the code and look under Create panel>Geometry>HowTo category in the drop-down list. The plug-in will be only available for the single session and will not be there after restarting 3ds Max.

Any MAX files saved with the plug-in in the scene will require the script to be installed and evaluated in order to load correctly.

To use the plug-in, click the AntiStar button in the Create panel>Geometry>HowTo. Click in the viewport to define the center, drag to define the outer radius, click, drag again to define the inner radius, click, drag again to define the width. Click to finish or right-click to cancel.

Play with the controls in the UI under the Modify panel and note how quickly the mesh is updated by the script!

Back to

"How To" Tutorials Index Page