3ds Max does not provide a built-in function for drawing splines freely with the mouse. Surprisingly enough, the old Autodesk 3D Studio DOS had such a feature.
Using the power of MAXScript, we can implement this feature as a simple MacroScript. It will let us draw a spline in the ground plane using the mouse.
Related Topics:
Spline Shape Common Properties, Operators, and Methods
Picking Points in the Viewports
MAXScript Message and Query Dialogs
NATURAL LANGUAGE
Package the code as macroScript to be able to use as a button, menu item or shortcut.
Define a function to measure the distance between the last and current mouse position and add a new knot to the spline when a threshold is exceeded.
Define a drawing function to capture the mouse movement using the built-in pickPoint function.
Put the complete creation code in an undo context to be able to undo the drawing.
Create a new splineShape object.
Let the user click in the viewport to define the start of the spline.
If the user right-clicked, cancel the operation
If the user left-clicked, add a knot to the spline and call the draw function.
As long as the user moves the mouse, the pickPoint callback will keep on measuring the segment lengths and adding new knots when needed.
If the user clicked again, ask about closing the spline.
If answer is yes, close the spline, select it and exit the script
If answer is no, just select the spline and exit the script.
MAXSCRIPT
macroscript FreeSpline category:"HowTo" tooltip:"FreeSpline"
(
local old_pos
local new_spline
local second_knot_set
fn get_mouse_pos pen_pos old_pen_pos =
(
if old_pos == undefined then old_pos = old_pen_pos
if distance pen_pos old_pos > 10 then
(
if second_knot_set then
addKnot new_spline 1 #smooth #curve pen_pos
else
(
setKnotPoint new_spline 1 2 pen_pos
second_knot_set = true
)
old_pos = pen_pos
updateShape new_spline
)-- end if
)-- end fn
fn draw_new_line old_pen_pos =
(
pickPoint mouseMoveCallback:#(get_mouse_pos,old_pen_pos)
)
undo"Free Spline"on
(
new_spline = splineShape ()
old_pen_pos = pickPoint ()
if old_pen_pos == #RightClick then
(
delete new_spline
)
else
(
select new_spline
new_spline.pos = old_pen_pos
addNewSpline new_spline
addKnot new_spline 1 #smooth #curve old_pen_pos
addKnot new_spline 1 #smooth #curve old_pen_pos
second_knot_set = false
draw_new_line old_pen_pos
q = querybox "Close Spline?" title:"Free Spline"
if q then
(
close new_spline 1
updateshape new_spline
)
select new_spline
)--end else
)--end undo
)--end script
macroscript FreeSpline category:"HowTo" tooltip:"FreeSpline"
(
The macroScript will be called FreeSpline
. To use the script, you can go to Customize... and drag the script from the category "HowTo" to a toolbar, a menu, a quad menu or assign to a keyboard shortcut.
local old_pos
local new_spline
local second_knot_set
We will need some local variables to store the last knot position and the new object created by the script. The third variable will be used as a flag to tell us whether we just started creation of the spline.
These variables will exist only inside the macroScript’s context as we declare them as local.
fn get_mouse_pos pen_pos old_pen_pos =
(
We create our own function to handle the drawing of new knots in the splineShape. This function receives two parameters – the current position of the mouse, and the result of the first click captured by the pickPoint function (see below).
if old_pos == undefined then old_pos = old_pen_pos
If we don’t know of an existing Knot (creation just started!), we set the value of the old_pos local variable to the first clicked point provided by the old_pen_pos parameter.
if distance pen_pos old_pos > 10 then
(
Next we check the distance between the last known knot’s position and the current mouse position. If the distance is longer than 10 units...
if second_knot_set then
The flag variable we defined will contrain either true or false. When the variable contains false, spline creation has just started. Because a spline must have at least two knots (vertices), we will create the first knot twice and then set this flag to false. When the next knot position is to be set, instead of creating a new one, we will reposition the existing second knot to the new coordinates and then set the flag to true.
When the flag is set to true, we will just create the next new knot - see next line...
addKnot new_spline 1 #smooth #curve pen_pos
...we go on and place a new knot, thus adding a new segment to the spline shape. The addKnot function requires a splineShape object, the index of the spline (we have just 1), the type of the knot (we use #smooth in our case), and the position to put the knot at.
else
(
setKnotPoint new_spline 1 2 pen_pos
When the flag is set to false, we have to move the existing second knot instead of creating a new one...
second_knot_set = true
Then we set the flag to true. Next time, a new knot will be created instead...
)
old_pos = pen_pos
Then we remember the new knot’s position to use by the next mouse movement check.
updateShape new_spline
To see the changes made to the spline, we update the splineShape object.
) -- end if
) -- end fn
fn draw_new_line old_pen_pos =
(
We create another function to handle the capturing of the mouse motion.
pickPoint mouseMoveCallback:#(get_mouse_pos,old_pen_pos)
Our function calls only the built-in pickPoint function which provides an optional callback which calls the function we just defined (get_mouse_point) and passes the current mouse position and any user parameters supplied (in our case, old_pen_pos).
As long as the mouse is moving and no clicks are encountered, the pickPoint’s mouseMoveCallback will constantly call our function and thus create new knots at distance of 10 units...
Picking Points in the Viewports
)
undo "Free Spline" on
(
We will put the complete code in an undo context to be able to undo the whole drawing result.
new_spline = splineShape ()
When starting the script, we first construct an empty splineShape object.
old_pen_pos = pickpoint ()
Then we go into pickPoint mode (but this time without a mouse callback). The function will wait for a click in the viewport and return the position of the picked point, or #RightClick if the Right Mouse Button has been pressed.
Picking Points in the Viewports
if old_pen_pos == #RightClick then
(
delete new_spline
)
If the Right Mouse Button has been actually pressed, we just delete the newly created splineShape and finish the script.
else
(
select new_spline
If the pickButton has returned a position value and not #RightClick, we can select the splineShape...
new_spline.pos = old_pen_pos
Set the position of the splineShape to the clicked point...
addNewSpline new_spline
...add a new spline to the empty splineShape...
addKnot new_spline 1 #smooth #curve old_pen_pos
addKnot new_spline 1 #smooth #curve old_pen_pos
...and add two new knots to the newly create spline in the splineShape. As mentioned above, a spline should have at least two knots, so we create the first two with the same position and will then move the second knot instead of creating a new one.
second_knot_set = false
We "lower the flag" by setting it to false. This way, the script will know that we just created the first two knots and the second one has to be set to the next mouse position captured by the script. When this is done in the get_mouse_pos
function, we will "raise the flag" again by setting it to true and thus know that the initial spline creation steps are over...
draw_new_line old_pen_pos
Now we can call our draw_new_line
function and pass the first clicked point as parameter. That function will start monitoring the mouse movements until a new click occurs.
q = querybox "Close Spline?" title:"Free Spline"
If a new click has been captured by the pickPoint
function, the draw_new_line
function will return to this point. Now we can ask the user whether he wants to close the newly drawn spline.
MAXScript Message and Query Dialogs
if q then
(
close new_spline 1
updateshape new_spline
If the answer to the query is Yes, we close the first and only spline in the splineShape and update the internal data structures to reflect the changes.
)
select new_spline
Finally, we select the newly-created splineShape in the scene before exiting.
)--end else
)--end undo
)--end script
Evaluate the script. To use it, you can use Customize... to drag the script from the category "HowTo" to a toolbar, a menu, a quad menu or to assign to a keyboard shortcut.
Execute the FreeSpline script and left-click in the top or perspective viewport. Drag the mouse freely to draw a spline. Left-click again to finish drawing. Answer the prompt with Yes to close or No to leave the spline open.
This is just some basic code. You could try to add a UI with controls for segment length threshold, spline segment and knot types (linear, smooth, bezier etc.)
Back to