Tool clauses define the components of a tool and can be one of two basic things:
variables, functions or structure definitions
variables, functions, and structures whose scope is the tool.
Event handlers are special kinds of functions associated with particular events. Event handlers specify the processing you want to occur when a user clicks or moves a mouse, right-clicks, or starts or stops the tool. These actions generate named events and the optional event handler you supply for that event is called when the action occurs.
Formally, the syntax of
Global variables cannot be declared as a tool clause, however they can be declared within event handler scripts. If you need to ensure a variable name references a global variable, declare the variable name as global immediately before defining the tool in your script.
When writing scripts, it is good programming practice to explicitly declare your local and global variables. Implicit declaration is provided as a short-hand, typically used when working in the Listener interactively or developing short scripts. When developing extended scripts, explicitly declaring variables can reduce errors and improve readability of the code. It is also recommend that you declare as local all variables unless you really want them to be global variables. The reasons for this are described in Scope of Variables.
Within the functions and event handlers of a tool, 13 pre-defined locals variables are always accessible. Each one holds a useful value relating to the mouse action that just occurred:
The current mouse position in viewport pixel coordinates.
The current mouse position projected on the active grid in world coordinates.
The distance in X, Y, and Z from the previous click point to the current click point in world coordinates.
The angles around the world X, Y, and Z axes from the previous click point to the current click point in world coordinates.
The current mouse position projected on the active grid in the coordinate system of the active grid. If the active grid is the home grid, the coordinate system is the home grid plane active in the current viewport (that is, the construction plane). See the discussion of grid versus world values below.
The distance in X, Y, and Z from the previous click point to the current click point in the coordinate system of the active grid. If the active grid is the home grid, the coordinate system is the home grid plane active in the current viewport (that is, the construction plane). See the discussion of grid versus world values below.
The angles around the world X, Y, and Z axes from the previous click point to the current click point in the coordinate system of the active grid. If the active grid is the home grid, the coordinate system is the home grid plane active in the current viewport (that is, the construction plane). See the discussion of grid versus world values below.
true if SHIFT key was down.
true if CTRL key was down.
true if ALT key was down.
true if left mouse button was down.
true if middle mouse button was down.
true if right mouse button was down.
When a create mouse tool is used in a level 5 scripted plug-in, an additional local variable is accessible:
Provides read/write access to the transform of the node currently being created. This value is in the current grid coordinate system.
See Scripted Plug-ins for more information.
When writing SimpleObject scripted plug-ins, you should always use the gridPoint, gridDist, and gridAngle values rather than corresponding world values, as object-creation in SimpleObject scripted plug-ins is managed in the active grid coordinate system.
For gridDist, the .X and .Y components are the delta X and Y between the previous clicked point and the current mouse point in the plane of the current grid. The .Z component is a projection from the current Y screen coordinates onto a Z-vector (in the grid coordinate system) based at the last point clicked on the grid, such that the .Z value is the projected height up the Z-vector. For non-orthogonal viewports this is as expected, but for orthogonal viewports (in which you are always dragging in the XY plane of the grid), this is always the same as gridDist.y. If you are using the gridDist value to build the portion of an object on the grid, or determine the distance on the current grid from the last point, you want to use only the .X and .Y components.
side1Len = gridDist.x
side2Len = gridDist.y
If you are specifying the height off of the current grid, you would typically use the .Z component.
height = gridDist.z
For worldDist, the behavior is similar to that for gridDist, however the projected component is dependent on the construction plane of the viewport. For the Top, Bottom, and non-orthogonal viewports, the projected height is contained in the .Z component. For Left and Right viewports, the projected height is contained in the .X component. For Front and Back viewports, the projected height is contained in the .Y component.
When you create a node in the 3ds Max user-interface, the local Z axis of the node is perpendicular to the construction plane. When you create a node using MAXScript, by default the local Z axis of the node points along the direction of the world Z axis. If you create a node in a tool (other than in a SimpleObject scripted plug-in), you must take into account the construction plane orientation if you want to duplicate 3ds Max’s behavior when creating a node. The easiest way to do this is to create the node in a coordsys grid context and specify the position of the node during creation in grid coordinate space. An example of this is shown in the following script.
local p, createpoint
-- define a function to perform actual node creation. Setting coordsys to
-- ‘grid’ in order for the alignment of the node’s local Z axis to be
-- perpendicular to the construction grid
fn createpoint = in coordsys grid p=point pos:gridPoint
-- set up so that a node is created on a mouse button down, move node
-- drag, release node at mouse button up.
-- if clickno == 1, then we are at first mouse click, which is a mouse
-- button down. If clickno != 1, at following mouse button up.
on mousePoint clickno do
if clickno == 1
-- if p == undefined, then clicked twice without mouse movement
-- (double clicked). No point object present, so just ignore this click.
else if p != undefined do (p.pos=worldPoint;p=undefined)
-- if p != undefined, we are moving a previously created node
-- if p == undefined, and left mouse button is down, create a node
on mouseMove clickno do
if p != undefined
else if lbutton do createPoint()
-- start the tool. No exit condition defined, so right-click to exit.
Called when the tool starts.
Called when the tool ends.
Called when the mouse moves prior to the first click.
Called for each mouse click, parameter arg contains the number of the click.
Called when the mouse moves anytime after the first click.
Called when the user right-clicks to cancel or presses the ESC key.
In 3ds Max 6 and higher, while running a mouse tool, if you click in a viewport where the construction plane is on edge, the tool will exit with a return value of #abort
The following script simply demonstrates when the various event handlers are called. Simply run this script and drag in the viewports. Right-click or press the ESC key to stop the tool’s execution.
on freeMove do print "Free Moving"
on mousePoint clickno do format "Point: %\n" clickno
on mouseAbort clickno do format "Abort: %\n" clickno
on mouseMove clickno do format "Moving: %\n" clickno
on start do print "Starting"
on end do print "Ending"
startTool foo prompt:"Hello!"
All of the mouse action event handlers may return the special value #stop which causes the tool to stop. In the following mousePoint event handler example, the first click creates the box and the release (second click) terminates the tool.
on mousePoint clickno do
if clickno == 1
then b = box pos:worldPoint
The following script implements the following functions: on mouse click three spot lights are created at the mouse point. While holding the mouse button down, drag the mouse to position the classical key, back and fill light positions. During this drag, holding down the SHIFT key will flip the fill light side. Release the mouse button and move the mouse to change the elevation of the lights. Click the mouse button when the lights are at the correct height. The back light is raised 1.5 times the key light’s height, and the fill light is raised 0.75 times the key light’s height.
To start this tool, you would say:
and then drag in one of the viewports.
local key, fill, back, targ
on mousePoint click do coordsys grid
if click == 1 then -- create key, back & fill lights at mousedown
key = targetspot pos:gridPoint name:"key" target:targ
back = targetspot pos:gridPoint name:"back" target:targ
fill = targetspot pos:gridPoint name:"fill" target:targ
if click == 3 then #stop
on mouseMove click do
if click == 2 then -- drag out & round on x-y plane
coordsys grid key.pos = gridPoint
coordsys targ back.pos = - key.pos
local p = if shiftKey then 90 else -90
coordsys targ fill.pos = key.pos * ((eulerangles 0 0 p) as quat)
else if click == 3 then -- drag up to elevate lights
in coordsys targ
local Z = gridDist.z
key.pos.z = Z
back.pos.z = Z * 1.5
fill.pos.z = Z * 0.5
Scripted Mouse Tools