Why ArrowHelper extending Helper and not mesh?


#1

I came acros this old thread and was wondering which advantage it has, that ArrowHelper extends dummy and not mesh. Could anybody explain it to me?


#2

probably keeps it “classified” as a helper to the rest of max


#3

So this is the only reason? No other advantages?


#4

some advantages are ‘not convertable’, ‘not deformable’, ‘not renderable’
also helpers and meshes go in different selection and visibility categories

which fits better to such things as helpers-locators-bones-joints of a custom rig for example


#5

By ‘not convertable’ you mean it can´t be coverted to mesh or poly? If yes, this the advantage I was looking for.
Could you please also explained what exactly you meaning by ‘not deformable’ and ‘not renderable’?


#6

to max it’s a helper, so doesn’t appear in any renders and if it has a deforming modifier applied it remains unchanged.


#7

But the mesh is still displaied and a material can be set for it, right?


#8

You can try it yourself to make sure

… and ideally publish your code


#9

Would like to try it, but still not completly understanding it. found an other similar example here, but why that local variables used?


#10

so it doesn’t have to rebuild the mesh unless the size changes.

this how i do it fif i’m prototyping for the sdk…

plugin Helper arrow_helper
	name:"arrow_helper"
	classID:#(0x45c33d08, 0x16e70cb1)
	category:"Standard"
	extends:dummy
	replaceUI:true;
(
	local lastSize, msh = trimesh();
	
	parameters main rollout:params
	(
		size type:#float ui:ui_size default:10.0;
	)
	
	rollout params "Params"
	(
		spinner ui_size "Icon Size:" range:[0, 100000000.0, size] fieldwidth:64 type:#float align:#center;
	)

	tool create
	(
 		on mousePoint click do case click of
 		(
 			1: 
 			(
 				nodeTM.translation = gridPoint;
 				size = 0.01;
 			)
 			2: #stop
 		)
 		on mouseMove click do case click of
 		(
 			2:  size = amax (abs gridDist.x) (abs gridDist.y);
 		)
	)
		
	fn mxssetedgevisflags m face flags =
	(	
		setEdgeVis m face 1 flags[1];
		setEdgeVis m face 2 flags[2];
		setEdgeVis m face 3 flags[3];
	)

	fn BuildMesh =
	(
		setNumVerts msh 13
		setNumFaces msh 22
		setvert msh 1 ([0.5,0,0] * size)
		setvert msh 2 ([0,0.5,0] * size)
		setvert msh 3 ([-0.5,0,0] * size)
		setvert msh 4 ([0,-0.5,0] * size)
		setvert msh 5 ([0.5,0,6.99999] * size)
		setvert msh 6 ([0,0.5,6.99999] * size)
		setvert msh 7 ([-0.5,0,6.99999] * size)
		setvert msh 8 ([0,-0.5,6.99999] * size)
		setvert msh 9 ([1.5,0,7.00001] * size)
		setvert msh 10 ([0,1.5,7.00001] * size)
		setvert msh 11 ([-1.5,0,7.00001] * size)
		setvert msh 12 ([0,-1.5,7.00001] * size)
		setvert msh 13 ([0,0,10] * size)
		setface msh 1 [1,2,6]
		setface msh 2 [6,5,1]
		setface msh 3 [2,3,7]
		setface msh 4 [7,6,2]
		setface msh 5 [3,4,8]
		setface msh 6 [8,7,3]
		setface msh 7 [4,1,5]
		setface msh 8 [5,8,4]
		setface msh 9 [3,2,1]
		setface msh 10 [1,4,3]
		setface msh 11 [7,8,12]
		setface msh 12 [7,12,11]
		setface msh 13 [6,7,11]
		setface msh 14 [6,11,10]
		setface msh 15 [5,6,10]
		setface msh 16 [5,10,9]
		setface msh 17 [8,5,9]
		setface msh 18 [8,9,12]
		setface msh 19 [9,10,13]
		setface msh 20 [10,11,13]
		setface msh 21 [11,12,13]
		setface msh 22 [12,9,13]
		mxssetedgevisflags msh  1 #{1..2}
		mxssetedgevisflags msh  2 #{1..2}
		mxssetedgevisflags msh  3 #{1..2}
		mxssetedgevisflags msh  4 #{1..2}
		mxssetedgevisflags msh  5 #{1..2}
		mxssetedgevisflags msh  6 #{1..2}
		mxssetedgevisflags msh  7 #{1..2}
		mxssetedgevisflags msh  8 #{1..2}
		mxssetedgevisflags msh  9 #{1..2}
		mxssetedgevisflags msh  10 #{1..2}
		mxssetedgevisflags msh  11 #{1}
		mxssetedgevisflags msh  12 #{2}
		mxssetedgevisflags msh  13 #{1}
		mxssetedgevisflags msh  14 #{2}
		mxssetedgevisflags msh  15 #{1}
		mxssetedgevisflags msh  16 #{2}
		mxssetedgevisflags msh  17 #{1}
		mxssetedgevisflags msh  18 #{2}
		mxssetedgevisflags msh  19 #{1..3}
		mxssetedgevisflags msh  20 #{1..3}
		mxssetedgevisflags msh  21 #{1..3}
		mxssetedgevisflags msh  22 #{1..3}
		update msh
	)
	
	on getDisplayMesh do 
	(
		if size != lastSize  do 
		(
			buildmesh();
			lastSize = size;
		)
		msh;
	)
)

and I use this snippet to convert a mesh to mxs

fn mxsgetedgevisflags obj f = 
(
	flags = #{}
	for i = 1 to 3 do flags[i] = getEdgeVis obj f i; 	
	flags;
)	

fn MeshtoMXS obj = if classof obj == editable_mesh do
(	
	itm = inverse obj.transform;
	numverts = getNumVerts obj;
	numfaces = getNumFaces obj;
	
	format "setNumVerts msh %\n" numverts;
	format "setNumFaces msh %\n" numfaces;
	
	for v = 1 to numverts do format "setvert msh % (% * size)\n" v (getVert obj v * itm);
	for f = 1 to numfaces do format "setface msh % %\n" f (getFace obj f);
	for f = 1 to numfaces where not (ev = mxsgetedgevisflags obj f).isEmpty do format "mxssetedgevisflags msh  % %\n" f ev;
)

#11

yep… and what is more important we have to use a local instance of the mesh to not create a new one if it’s not necessary, because it takes time and eats memory


#12

in your example it’s also not necessary to rebuild whole mesh on size changed. you can just move (transform, scale) the mesh instance verts …
ideally we should rebuild mesh only in topology changed case

memory leaking is a big issue for this type of scripted helpers


#13
plugin Helper arrow_helper
name:"arrow_helper"
classID:#(0x45c33d08, 0x16e70cb1)
category:"Standard"
extends:dummy
replaceUI:true;
    (
    	local lastSize, msh = trimesh(), initialized = false;
    	
    	parameters main rollout:params
    	(
    		size type:#float ui:ui_size default:10.0;
    	)
    	
    	rollout params "Params"
    	(
    		spinner ui_size "Icon Size:" range:[0, 100000000.0, size] fieldwidth:64 type:#float align:#center;
    	)

    	tool create
    	(
     		on mousePoint click do case click of
     		(
     			1: 
     			(
     				nodeTM.translation = gridPoint;
     				size = 0.01;
     			)
     			2: #stop
     		)
     		on mouseMove click do case click of
     		(
     			2:  size = amax (abs gridDist.x) (abs gridDist.y);
     		)
    	)
    		
    	fn setedgevisflags m face flags =
    	(	
    		setEdgeVis m face 1 flags[1];
    		setEdgeVis m face 2 flags[2];
    		setEdgeVis m face 3 flags[3];
    	)

    	fn BuildMesh =
    	(
    		if not initialized then
    		(	
    			setNumVerts msh 13
    			setNumFaces msh 22
    			setface msh 1 [1,2,6]
    			setface msh 2 [6,5,1]
    			setface msh 3 [2,3,7]
    			setface msh 4 [7,6,2]
    			setface msh 5 [3,4,8]
    			setface msh 6 [8,7,3]
    			setface msh 7 [4,1,5]
    			setface msh 8 [5,8,4]
    			setface msh 9 [3,2,1]
    			setface msh 10 [1,4,3]
    			setface msh 11 [7,8,12]
    			setface msh 12 [7,12,11]
    			setface msh 13 [6,7,11]
    			setface msh 14 [6,11,10]
    			setface msh 15 [5,6,10]
    			setface msh 16 [5,10,9]
    			setface msh 17 [8,5,9]
    			setface msh 18 [8,9,12]
    			setface msh 19 [9,10,13]
    			setface msh 20 [10,11,13]
    			setface msh 21 [11,12,13]
    			setface msh 22 [12,9,13]
    			setedgevisflags msh  1 #{1..2}
    			setedgevisflags msh  2 #{1..2}
    			setedgevisflags msh  3 #{1..2}
    			setedgevisflags msh  4 #{1..2}
    			setedgevisflags msh  5 #{1..2}
    			setedgevisflags msh  6 #{1..2}
    			setedgevisflags msh  7 #{1..2}
    			setedgevisflags msh  8 #{1..2}
    			setedgevisflags msh  9 #{1..2}
    			setedgevisflags msh  10 #{1..2}
    			setedgevisflags msh  11 #{1}
    			setedgevisflags msh  12 #{2}
    			setedgevisflags msh  13 #{1}
    			setedgevisflags msh  14 #{2}
    			setedgevisflags msh  15 #{1}
    			setedgevisflags msh  16 #{2}
    			setedgevisflags msh  17 #{1}
    			setedgevisflags msh  18 #{2}
    			setedgevisflags msh  19 #{1..3}
    			setedgevisflags msh  20 #{1..3}
    			setedgevisflags msh  21 #{1..3}
    			setedgevisflags msh  22 #{1..3}
                        initialized = true	
    		)
    		
    		setvert msh 1 ([0.5,0,0] * size)
    		setvert msh 2 ([0,0.5,0] * size)
    		setvert msh 3 ([-0.5,0,0] * size)
    		setvert msh 4 ([0,-0.5,0] * size)
    		setvert msh 5 ([0.5,0,7] * size)
    		setvert msh 6 ([0,0.5,7] * size)
    		setvert msh 7 ([-0.5,0,7] * size)
    		setvert msh 8 ([0,-0.5,7] * size)
    		setvert msh 9 ([1.5,0,7] * size)
    		setvert msh 10 ([0,1.5,7] * size)
    		setvert msh 11 ([-1.5,0,7] * size)
    		setvert msh 12 ([0,-1.5,7] * size)
    		setvert msh 13 ([0,0,10] * size)
    		update msh
    	)
    	
    	on getDisplayMesh do 
    	(
    		if size != lastSize  do 
    		(
    			buildmesh();
    			lastSize = size;
    		)
    		msh;
    	)
    )

#14

@denisT Sory what I meand is just something like this:

plugin Helper arrow_helper
name:"arrow_helper"
classID:#(0x45c33d08, 0x16e70cb1)
category:"Standard"
extends:dummy
replaceUI:true;
    (
    	msh = trimesh()
            ...
    )

I mean without local modifier.


#15

also another difference between hanging the mesh off a dummy , forcing it to be a helper, and not a simpleObject is that it can only ever be displayed in the viewports as backface culled wireframe when a helper.


#16

So a red_arrow_helper, which should display a red arrow, is not posible?
Also tried that HelperTest_DisplayMesh example from max help, but when triing to change the size of that box it always crashes and the problem seems to be in this line:

lastSize = size

At least the error mesage saies something about a problem wit “=” operator between lastSize and size.


#17

Delete both ‘\’ characters in the lines above the two lastSize = size occurrencies.


#18

always red arrow

plugin Helper arrow_helper
	name:"arrow_helper"
	classID:#(0x45c33d08, 0x16e70cb1)
	category:"Standard"
	extends:dummy
	replaceUI:true;
(
	local lastsize, msh = trimesh(), initialized = false, this_node = undefined;
	
	parameters main rollout:params
	(
		size type:#worldunits ui:ui_size default:10.0;
	)
	
	rollout params "Params"
	(
		spinner ui_size "Size:" range:[0, 100000000.0, size] fieldwidth:64 type:#worldunits align:#right;
	)

	tool create
	(
 		on mousePoint click do case click of
 		(
 			1: 
 			(
 				nodeTM.translation = gridPoint;
 				size = 0.01;
 			)
 			2: #stop
 		)
 		on mouseMove click do case click of
 		(
 			2:  size = amax (abs gridDist.x) (abs gridDist.y);
 		)
	)
		
	fn setedgevisflags m face flags =
	(	
		setEdgeVis m face 1 flags[1];
		setEdgeVis m face 2 flags[2];
		setEdgeVis m face 3 flags[3];
	)

	fn BuildMesh =
	(
		if not initialized then
		(	
			setNumVerts msh 13;
			setNumFaces msh 12;
			setface msh 1 [1,2,6];
			setface msh 2 [6,5,1];
			setface msh 3 [2,3,7];
			setface msh 4 [7,6,2];
			setface msh 5 [3,4,8];
			setface msh 6 [8,7,3];
			setface msh 7 [4,1,5];
			setface msh 8 [5,8,4];
			setface msh 9 [9,10,13];
			setface msh 10 [10,11,13];
			setface msh 11 [11,12,13];
			setface msh 12 [12,9,13];
			setedgevisflags msh  1 #{1..2};
			setedgevisflags msh  2 #{1..2};
			setedgevisflags msh  3 #{1..2};
			setedgevisflags msh  4 #{1..2};
			setedgevisflags msh  5 #{1..2};
			setedgevisflags msh  6 #{1..2};
			setedgevisflags msh  7 #{1..2};
			setedgevisflags msh  8 #{1..2};
			setedgevisflags msh  9 #{1..3};
			setedgevisflags msh  10 #{1..3};
			setedgevisflags msh  11 #{1..3};
			setedgevisflags msh  12 #{1..3};
			initialized = true;
		)
		
		setvert msh 1 ([0.05,0,0] * size);
		setvert msh 2 ([0,0.05,0] * size);
		setvert msh 3 ([-0.05,0,0] * size);
		setvert msh 4 ([0,-0.05,0] * size);
		setvert msh 5 ([0.05,0,0.7] * size);
		setvert msh 6 ([0,0.05,0.7] * size);
		setvert msh 7 ([-0.05,0,0.7] * size);
		setvert msh 8 ([0,-0.05,0.7] * size);
		setvert msh 9 ([0.15,0,0.7] * size);
		setvert msh 10 ([0,0.15,0.7] * size);
		setvert msh 11 ([-0.15,0,0.7] * size);
		setvert msh 12 ([0,-0.15,0.7] * size);
		setvert msh 13 ([0,0,1] * size);
				
		update msh;
	)
	
	on getDisplayMesh do 
	(
		if size != lastsize then
		(
			buildmesh();
			lastsize = size;
		)
		msh;
	)
	
	on attachedToNode n do this_node = n;
	on detachedFromNode n do this_node = undefinedl	
	on useWireColor do 
	(	
		if this_node != undefined then this_node.wirecolor = red;
		true;
	)	
)

#19

Sorry my error again, was tallking about a solid red arrow, not a wire one. But thanks for the code, it looks interesting.


#20

you can’t draw a solid mesh in a helper plugin. it’s a limitation.

check the code below:

plugin Helper AdvHelperBox
	name:"AdvHelperBox"
	classID:#(0x47db14fe, 0x4e9b5f99)
	category:"Standard"
	extends:dummy
(
	struct mesh_data_struct (obj, mesh, size)
	
	local mesh_data, test_var
	
	parameters pblock rollout:params
	(
		size type:#float ui:ui_amount default:40.0
	)
	rollout params "HelperTest Parameters"
	(
		spinner ui_amount "Size:" range:[0, 1e9, 40]
	)
	on getDisplayMesh do
	(
		format "display mesh - %\n" (timeStamp())
		
		if not isstruct mesh_data do 
		(
			mesh_data = mesh_data_struct()
		)
		if not iskindof mesh_data.obj Box do
		(
			mesh_data.obj = createInstance box mapCoords:false
			mesh_data.size = undefined
		)
		if (mesh_data.size != size) do
		(
			format "build mesh - %\n" (timeStamp())
			
			mesh_data.obj.length = mesh_data.obj.width = mesh_data.obj.height = size
			mesh_data.mesh = mesh_data.obj.mesh
		)
		mesh_data.size = size
		mesh_data.mesh
	)
	tool create
	(
		on mousePoint click do
		(
			nodeTM.translation = gridPoint
			#stop
		)
	)
	on update do
	(
		mesh_data = mesh_data_struct()
	)
)

i’ve just modified the code from the MXS help.
why?

the problem is - every helper node has more than simple geometry events which handle Mesh Build
for example a view redraw.
so we have to care about only reasonable mesh rebuild.

as you can see the output trace in the listener my plugin going very often in display event, but does rebuild only if size changed.
it’s very simple situation where we have to check only one parameter or condition. in real life the “rebuild reason” might be much more complicated.

another problem is every time we change any variable in plugins local scope it causes the update as well.
as you can see i put obj, mesh, and size into a struct variable. so all changes we do are in this struct scope, and it doesn’t cause the plugin update

try to change a “test” variable ‘test_var’

$.test_var = 0

it causes the plugin update, instead of after changing any mesh_data property:

$.mesh_data.size = 10