helper objects


Does anyone know how to create custom helper objects. I would like to create a dummy type object in the shape of an arrow.



Why ArrowHelper extending Helper and not mesh?

The MAXScript Reference shows how to create a scripted Helper plug-in with custom mesh display… You can build any mesh as in scripted geometry plugins and return it from the on GetDisplayMesh do () handler which was added in Max 7.
If you are using Max 6 or earlier, you cannot use that.


thanks bobo.

In order to create the mesh do i have to build it using script or can i build in the viewport conventionally. The reason i ask is because it would be alot quicker for me to build the object in the viewport rather than script it.




What I have done is written a script that reads in the data about an existing mesh and writes out the code about how it should be build. Then I can use that in the code for the helper.


I made the same thing, PEN – but there are a lot of exceptions and things it doesnt do. would you be willing to share that?



there’s Great script that will do that
with click of a button (some options included)

It’s scripted plugin called “Ghost”.



I just downloaded that Ghost plugin but it doesn’t seem to do this at all, it just makes a non-renderable version of the mesh…it does not generate the script that you would need.

Anyway, I have written a short one today that will generate the script for any spline object…I’m going to use it for this purpose, should be helpful.

fn readSpline ss=
 output = newscript()
 pos = getKnotPoint ss 1 1
 format "ss = SplineShape pos:% 
" pos to:output
 splineCount = (numSplines ss) 
 --splineCount = 1
 for iSpline = 1 to splineCount do
  format "addNewSpline ss 
" to:output
  knotCount = numKnots ss iSpline
  for iKnot = 1 to knotCount do
   type = getKnotType ss iSpline iKnot
   pos = getKnotPoint ss iSpline iKnot
   if (type==#bezier or type==#bezierCorner) then
	inVec = getInVec ss iSpline iKnot
	outVec = getOutVec ss iSpline iKnot
	format "addKnot ss % % #curve % % % 
" iSpline type pos inVec outVec to:output
   ) else
	format "addKnot ss % % #curve % 
" iSpline type pos to:output  
  format "close ss % 
" iSpline to:output
 format "updateShape ss 
" to:output


Ok, it’s not the same thing,

but I couldn’t agree with you that " it doesn’t seem to do this at all ".

It’s just a quick way for creating helpers.



You’re right, there are different ways to interpret his question…did he want to create a helper object for using in the scene, or as a plugin…


That ghost script does the same as mine and I also have one for spline for creating control objects. I have to go back and see if I can add more things to my helper creator and clean it up into a tool so that people know what to do with it. At the moment it is like most of my scripts and it is a collection of functions.


Heya nebille,

Michale comet has written a tool called spline to mxs which will output the maxscript code to make whatever spline shape you currently have selected - you can draw your custom shape, use the spline2mxs script to output its code and then anywhere you have a position for a vertex in the spline, make sure you put in a multiplier variable to control the scale of the shape so the following co-ordinates for a point [100.00,350.00,500.00] become [100.00scale,350.00scale,500.00*scale] - the scale value can be controlled using the mousetool command in maxscript - you record the point the user has clicked on to begin making the custom shape, and then record the distance their mouse has travelled whicle holding the mouse button - the difference between these two values drives the shape variable and your custom shape will be created in the viewport as you drag. I’ve done this with a few simple shapes for my own helpers and it worked fairly well.



Have a look at my down loadable control objects for Max as well for reference on using the drag and drop method with mouse tools.


I’m having some trouble getting this scripted helper objects to work.

Basically I want a scripted plugin to be able to use it’s own helper object. So, I am first trying to make the helper object plugin.

To make a helper object plugin It seems that I have to override getDisplayMesh to createInstance of some object, then return that object.mesh, and also set useWireColor to false. I can script the creation of an object, but I can’t use createInstance on that…so it seems that I need to first make a geometryPlugin of the shape, so that I can create an instance of it to use in the helper object, to use in the real plugin. I will make the first 2 be invisible plugins.

The geometry plugin works fine, and I set the scripted geometry object to be the mesh local var of this plugin.

Then in the helper plugin, I try to create an instance of this but it says “attempt to access deleted scene object” and then gives me an error when I try to create something.


No, you are thinking to complicated. The only thing the getDisplayMesh needs is a TriMesh value, and it does not matter where it comes from.
You can take it from an instance, or you can build it yourself inside the plugin by providing a list of vertices and faces, just like you can do with simple geometry plugins.
What people suggested here was to take an existing scene object, snapshot its vertex and face data, write to two arrays and hardcode these arrays into the plugin’s source.

Here is an example:

plugin Helper ArrowHelper
classID:#(0x47db14ff, 0x4e9b5f92) 
local lastSize, meshObj, theMesh, lastHeadWidth, lastHeadHeight, lastStemWidth 

parameters pblock rollout:params
	size type:#float animatable:true ui:size default:1.0
	headWidth type:#float animatable:true ui:headWidth default:0.5
	headHeight type:#float animatable:true ui:headHeight default:0.5
	stemWidth type:#float animatable:true ui:stemWidth default:0.3

rollout params "Arrow Helper Parameters"
	spinner size "Size:" range:[0, 1e9, 1]
	spinner headWidth "Head Width:" range:[0, 1.0, 0.5] scale:0.01
	spinner headHeight "Head Height:" range:[0, 1.0, 0.5] scale:0.01
	spinner stemWidth "Stem:" range:[0, 1.0, 0.3] scale:0.01

on getDisplayMesh do 
    if theMesh == undefined OR size != lastSize OR lastHeadWidth != headWidth OR  lastHeadHeight != headHeight OR lastStemWidth != stemWidth do 
    	local vertsArray = #( [0,0,0], [headWidth,-headHeight ,0], [stemWidth,-headHeight ,0], [stemWidth,-1,0],[-stemWidth,-1,0],[-stemWidth,-headHeight ,0],[-headWidth,-headHeight ,0])
		local facesArray = #([1,7,6], [1,3,2], [3,5,4], [3,6,5])
    	local edgeVis = #( #(true,true,false), #(false,true,true), #(false,true,true), #(false,true,false) )
		meshObj = mesh vertices:(for v in vertsArray collect v*size) faces:facesArray 
		for face = 1 to edgeVis.count do 
			for i = 1 to 3 do 
				setEdgeVis meshObj face i edgeVis[face][i] 
		theMesh = meshObj.mesh
		delete meshObj
		lastSize = size
		lastHeadWidth = headWidth 
		lastHeadHeight = headHeight
		lastStemWidth = stemWidth 
)--end getDisplayMesh

tool create
	on mousePoint click do
		case click of
			1: nodeTM.translation = gridPoint
			2: #stop
	on mouseMove click do
		case click of
			2: (size = length gridDist)
)--end tool create
)--end plugin


Hey Bobo, I just tried the code and it crashed Max out. Does it work at your end?


It was in Max 8 that it died. Max 7 appears to work just fine.


I love reading your code Bobo, I always learn a little something. It is always at least one line shorted then mine and just a bit cleaner. Thanks for posting that. I just need to clean up some of mine now.


hi bobo

I cant thank you enough for your informative codes. Im learning so much from you. Thanks once again for the script. I will now read through it and gain an understanding of what you have done

many thanks



Thanks Bobo, I will have a thorough read through of this soon!

For now, the script seems to crash from this:

theMesh = meshObj.mesh
		delete meshObj

You have to change it to:

theMesh = copy meshObj.mesh

then it will work in Max 8. I didn’t try it in 7…maybe they changed how this function works or something. You could probably also leave out the copy statement if you changed theMesh value because then max should implicitly make a copy of it


Thanks, I assume there is a change in the Garbage Collector in 8 that causes that.
It runs great in 7…