Notifications
Clear all

[Closed] Scripted Plugin trimesh properties

So i’m having a problem getting simple properties of a trimesh made in a simpleObject scripted plugin. Is there any properties list for trimeshes? I was looking pretty hard with no results. I noticed simple things that can be done with other nodes can’t be done with the trimesh, using the same commands. Namely I am looking for getting the center, looping, selecting, getting selections, and connecting edges. I was also wondering about creating objects other than by dragging? It seems like trimeshes from simple object plugins are very limited.
Thanks for any responses
D3Mi

17 Replies

is this for the picture frame building script ?

Yes I misunderstood that a scriptedplugin used a trimesh that worked completely different from an editable mesh. Imostly misunderstood plugins in general.

there’s no difference practically between a trimesh and an editable mesh.

from the mxs help.

[left]

The properties and methods described in this topic are applicable to both Editable_Mesh objects and TriMesh values are signified by a value type of <mesh>. The properties and methods applicable to just TriMesh values are signified by a value type of <trimesh>.

[/left]
[left]so everything you can do an Editable_Mesh you can do to a trimesh but not vise versa.

[/left]
[left]

[/left]

as I said on a previous thread it’s sometimes easier creating the mesh you want from scratch than trying to bend an existing mesh to your will. Cutting and slicing (editable meshes) is a nightmare keeping track of all the new faces edges and verts not to mention all the unintentional faces created It’s a bit easier with editable poly but still not pretty. Anyway I extended my simple picframe object a bit.

Its might seem abit longer and more complicated but still essentially the same principles, rows of verts in various position as the basis for rows of quads.

if you need to create this from a script (assuming the plugin script is installed in the \Autodesk&lt;Max Version>\PlugIns\scriptedplugins folder) you can use (I had to change denis’s profile code to use a tab param so the plugin could be create from mxs)


   pf = picframe()

will create a picframe obj with the default settings

pf = picframe width:100 height:50 size:10  wsegs:3 hsegs:2 zscale:2.0  cornersegs:true

will create a more tuned affair but I think you get the gist.

plugin simpleObject picframe
   	name:"picframe"
   	classID:#(0x68021924, 0x627ffe63)
   	category:"Scripted"
   (
   	--local params;
   	
   	parameters main rollout:params
   	(
   		curvedata 		type:#point3Tab tabSize:4  tabSizeVariable:true;
   		width			type:#float	  default:10.0 	ui:ui_width;
   		height		  type:#float	  default:10.0	ui:ui_height;
   		wsegs			type:#integer 	default:1	   ui:ui_wsegs;
   		hsegs			type:#integer	  default:1 		ui:ui_hsegs;
   		size			type:#float 	default:2.0 	ui:ui_size;
   		zscale		  type:#float	  default:1.0 	ui:ui_zscale;
   		mapCoords 		type:#boolean 	default:on 		ui:ui_mapCoords;
   		flipUV			type:#boolean 	default:off 	ui:ui_flipUV;	
   		contUV			type:#boolean 	default:off 	ui:ui_contUV;
   		picQuad			type:#boolean 	default:on 		ui:ui_picQuad;	
   		smoothframe		type:#boolean 	default:on 		ui:ui_smoothframe;
   		cornersegs 		type:#boolean 	default:off 	ui:ui_cornersegs;
   		manualUpdate 	type:#boolean 	default:off animatable:off ui:ui_manualUpdate;
   		
   		on curvedata set  arg1 arg2 do
   		(
   			--params.getProfile();
   		)	
   	)
   	
   -- our starting profile	
   	
   	fn defCurveData =
   	(	
   		curvedata.count = 4;
   		curvedata[1] = [0.0,0.0,0.0];			
   		curvedata[2] = [0.0,1.0,0.0];
   		curvedata[3] = [1.0,1.0,0.0];
   		curvedata[4] = [1.0,0.0,0.0];
   	)	
   	
   -- rollout	
   	
   	rollout params "Params"
   	(
   		local updating = on
   		spinner ui_width   "Width:" range:[0, 1e9, 0] fieldwidth:64 type:#float align:#right;
   		spinner ui_height  "Height:" range:[0, 1e9, 0] fieldwidth:64 type:#float align:#right;
   		spinner ui_wsegs   "Width Segs:" range:[1, 32, 1] fieldwidth:64 type:#integer align:#right offset:[0,4];
   		spinner ui_hsegs   "Height Segs:" range:[1, 32, 1] fieldwidth:64 type:#integer align:#right;
   		spinner ui_size	"Size:" range:[0, 1e9, 0] fieldwidth:64 type:#float align:#right offset:[0,4];
   		spinner ui_zscale  "Z Scale:" range:[-1e9, 1e9, 0] fieldwidth:64 type:#float align:#right;
   		checkbox ui_mapCoords "Generate Map Coords." align:#left offset:[-2,4];
   		checkbox ui_flipUV  "Flip UV Coords." align:#left offset:[-2,0];
   		checkbox ui_contUV "Continuous UV Coords." align:#left offset:[-2,0];
   		checkbox ui_picQuad "Create Picture Quad." align:#left offset:[-2,0];
   		checkbox ui_smoothframe "Smooth Frame." align:#left offset:[-2,0];
   		checkbox ui_cornersegs "Corner Segments." align:#left offset:[-2,0];
   		
   		group "Profile: "
   		(
   			CurveControl ui_profile numCurves:1 width:144 height:100 align:#left offset:[-4,2] enabled:on \
   				x_range:[0,1] y_range:[-0.5,1] x_value:0 \
   				scrollValues:[0,-10] zoomValues:[128,60] \
   				uiFlags:#(#constrainY, #noFilterButtons) \
   				rcmFlags:#(#move_xy, #move_x, #move_y, #corner, #delete) 
   			
   			checkbox ui_manualUpdate "Manual Update" align:#left offset:[-2,0]
   			button ui_updateProfile "Update" width:71 align:#left offset:[-4,0] tooltip:"Update Profile" across:2
   			button ui_resetProfile "Reset" width:71 align:#right offset:[4,0] tooltip:"Reset Profile"
   		)
   		
   -- CurveControl -> curvedata	
   		
   		fn setProfile updateMesh: = 
   		(
   			if updateMesh == unsupplied do updateMesh = not manualUpdate
   			
   			curve = ui_profile.curves[1]
   			curvedata.count = curve.points.count;
   			for i = 1 to curve.points.count do 
   			(
   				pnt = curve.points[i]
   				curvedata[i] = [pnt.value.x, pnt.value.y, 0.0]
   			)
   			if updateMesh do 
   			(
   				this.doBuildMesh()
   				redrawviews()
   			)
   		)
   		
   --  curvedata	-> CurveControl	
   		
   		fn getProfile = 
   		(
   			curve = ui_profile.curves[1];
   			curve.numPoints = curvedata.count;
   			for i =1 to curvedata.count  do 
   			(
   				pnt = curve.points[i]
   				cd = curvedata[i]
   				pnt.value = [cd.x, cd.y]
   				pnt.bezier = off
   				pnt.corner = on
   				if i == 1 or i == curvedata.count then pnt.lock_x = true;
   			)
   		)
   
   		on ui_profile deleted c val do if not loading and not updating do setProfile()
   		on ui_profile ptChanged c val do if not loading and not updating do setProfile()
   		on ui_profile tangentChanged c val type do if not loading and not updating do setProfile()
   		
   		fn resetProfile = 
   		(
   			defCurveData();
   			getProfile();
   		)
   		
   		on ui_updateProfile pressed do undo "Update Frame Profile" on  setProfile()
   		on ui_resetProfile pressed do undo "Reset Frame Profile" on  resetProfile()
   
   		on params open do
   		(
   			updating = on
   			deleteAllChangeHandlers id:#frame_callback
   			getProfile();
   			ccTarget = (refs.dependents ui_profile.curves[1])[1]
   			when topology ccTarget change id:#frame_callback do 
   			(
   				if ui_profile.curves[1].numpoints != curvedata.count do setProfile()
   			)
   			updating = off
   		)
   		on params close do  deleteAllChangeHandlers id:#frame_callback
   	)	
   
   	on create do  defCurveData();
   	
   -- viewport create	
   	
   	tool create
   	(
   		 on mousePoint click do case click of
   		 (
   			 1: 
   			 (
   				 nodeTM.translation = gridPoint
   				 width = 0.0;
   				height = 0.0;
   				size = 0.01;
   			 )
   			 3: #stop
   		 )
   		 on mouseMove click do case click of
   		 (
   			 2: 
   			(
   				width = 2.0 * abs gridDist.x;
   				height = 2.0 * abs gridDist.y;
   			)	
   			3: 
   			(
   				size = abs gridDist.x
   				zscale = abs gridDist.y
   			)	
   		 )
   	)
   		
   -- simple lerp
   	
   	fn lerp  a b s = (a + s * (b - a))	
   
   -- sets edge vis
   		
   	fn mxssetedgevisflags m face flags =
   	(	
   		setEdgeVis m  face 1 flags[1];
   		setEdgeVis m  face 2 flags[2];
   		setEdgeVis m  face 3 flags[3];
   	)
   	
   -- generate ucoords base on distances	
   	
   	fn GetUCoords =
   	(
   		ucoords = #();
   		ucoords.count = curvedata.count;
   		ucoords[1] = 0.0;
   		if curvedata.count == 2 then
   			ucoords[2] = 1.0;
   		else
   		(
   			tlen = 0.0;
   			p1 = curvedata[1]
   			for i = 2 to curvedata.count  do
   			(
   				p2 = curvedata[i]; 
   				tlen += distance p1 p2;
   				ucoords[i] = tlen;
   				p1 = p2;
   			)
   			for i = 1 to ucoords.count do ucoords[i] /= tlen; -- normalize to the total length
   		)		
   		ucoords	
   	)	
   
   -- generate V coords base on the x or y dimension	
   	
   	fn GetVCoords npsegs xdim =
   	(	
   		vcoords = #();
   		vcoords.count = npsegs + 1;
   		scalar = 0.5/size;
   		
   		for i = 1 to npsegs + 1  do
   		(
   			p = getvert mesh i;
   			if xdim then
   				vcoords[i] = scalar * abs p.x;
   			else
   				vcoords[i] = scalar * abs p.y;
   		)
   		vcoords;
   	)	
   
   -- create the mesh	
   	
   	fn doBuildMesh =
   	(
   		nlsegs = (wsegs + hsegs) * 2;
   		if cornersegs then nlsegs += 8;
   			
   		npsegs = curvedata.count - 1;
   		npsegs_p1 = npsegs + 1;
   		
   		nverts = npsegs_p1 * nlsegs;
   		nfaces = npsegs * nlsegs * 2;
   		
   -- offsets and sizes		
   		
   		w = width * 0.5;
   		h = height * 0.5;
   		s = size;
   		
   -- "cardinal" points that form the interior and exterior corners of the picture frame		  
   		
   		cp = #([-w-s,-h-s,0],[-w,-h,0], [w+s,-h-s,0],[w,-h,0], [w+s,h+s,0],[w,h,0], [-w-s,h+s,0],[-w,h,0]);
   		
   		setNumVerts mesh nverts;
   		setNumFaces mesh nfaces;
   		
   -- cache the side variations in a arrays so we can loop through them		
   		
   		segcounts = #(wsegs,hsegs,wsegs,hsegs);
   		seglenghts = #(width,height,width,height);
   		segstarts = #(-w,-h,w,h);
   		segdir = #(1,1,-1,-1);
   		posindex = #(1,2,1,2);
   		
   -- geo verts		
   		
   		pa = #();
   		pa.count = npsegs_p1;
   		vi = 1;	-- vert incrementer
   		for i = 1 to 4 do
   		(
   			pind = i*2;
   			p1 = cp[pind - 1];
   			p2 = cp[pind];	
   			
   -- cache the profile			
   			
   			for j = 1 to npsegs_p1  do
   			(
   				pa[j] = lerp p1 p2 (curvedata[j].x); 
   				pa[j].z = curvedata[j].y * zscale;
   			)	
   			
   -- the initial starting line of diagonal verts			
   			
   			for j = 1 to npsegs_p1  do
   			(
   				setvert mesh vi pa[j];
   				vi += 1;
   			)
   			if cornersegs then
   			(
   				pos = [0,0,0];
   				pos[posindex[i]] = segstarts[i];
   				for j = 1 to npsegs_p1  do
   				(
   					pos[3 - posindex[i]] = pa[j][3 - posindex[i]]; 	-- profile coord reverse of the lenght wise
   					pos.z = pa[j].z;
   					setvert mesh vi pos;
   					vi += 1;
   				)
   			)	
   					
   -- the length wize subdivisions			
   			
   			for k = 1 to segcounts[i] - 1 do
   			(
   				pos = [0,0,0];
   				pos[posindex[i]] = segstarts[i] +  segdir[i] * seglenghts[i] * k/segcounts[i];
   				for j = 1 to npsegs_p1  do
   				(
   					pos[3 - posindex[i]] = pa[j][3 - posindex[i]]; 	-- profile coord reverse of the lenght wise
   					pos.z = pa[j].z;
   					setvert mesh vi pos;
   					vi += 1;
   				)
   			)
   			if cornersegs then
   			(
   				pos = [0,0,0];
   				pos[posindex[i]] = -segstarts[i];
   				for j = 1 to npsegs_p1  do
   				(
   					pos[3 - posindex[i]] = pa[j][3 - posindex[i]]; 	-- profile coord reverse of the lenght wise
   					pos.z = pa[j].z;
   					setvert mesh vi pos;
   					vi += 1;
   				)
   			)	
   		)	
   -- geo faces			
   		fi = 1;	-- face incrementer
   		si_offset = 0;
   		ei_offset = npsegs_p1;
   	   starts = #(0, wsegs, wsegs + hsegs,2 * wsegs + hsegs, nlsegs);
  		if cornersegs then 
  			starts = #(0, 2 + wsegs,4 + wsegs + hsegs,6 + 2 * wsegs + hsegs, nlsegs + 2);
   		side = 0;
   		for i = 1 to nlsegs do
   		(	
   			if smoothframe then -- bit of a pain to compute the correct smoothing group
   			(		
   				smg = 2;
   				if i > starts[side+1] then 	side+=1;
   				if mod side 2 == 0 then smg = 4;	
   			)		
   			else
   				smg = 0;
   			
   			for j = 1 to npsegs  do
   			(
   				s1 = si_offset + j;
   				e1 = ei_offset + j;
   				s2 = s1 + 1;
   				e2 = e1 + 1;
   				
   				setface mesh fi [e1,e2,s1];
   				mxssetedgevisflags mesh fi #{1,3};
   				setFaceSmoothGroup mesh fi smg;
   				setFaceMatID mesh fi 2;
   				fi += 1;
   				
   				setface mesh fi [s2,s1,e2];
   				mxssetedgevisflags mesh fi #{1,3};
   				setFaceSmoothGroup mesh fi smg;
   				setFaceMatID mesh fi 2;
   				fi += 1;
   			)
   			si_offset += npsegs_p1;
   			ei_offset += npsegs_p1;
   			if i == nlsegs - 1 then ei_offset = 0; -- wrap around the mesh make the first vert row our end verts
   		)	
   	
   		if mapCoords then
   		(	
   			if contUV then
   			(	
   -- allocate mapping			
   			
   				ntverts = nverts + npsegs_p1;
   				meshop.setMapSupport mesh 1 true;
   				meshop.setNumMapVerts mesh 1 ntverts;
   				meshop.setNumMapFaces mesh 1 nfaces; 	
   
   -- mapping verts			
   				tlen = 2 * (width + height);
   				vcoords = #(0.0, width/tlen, (width + height)/tlen, (2.0 * width + height)/tlen, 1.0);
   				ucoords = GetUCoords();
   				vi = 1;
   				for i = 1 to 4 do 
   				(
   					v = vcoords[i];
   					for j = 1 to npsegs_p1 do	-- initial row of tverts
   					(
   						vv = v; -- restore v every loop
   						u = ucoords[j];
   						if flipUV then swap vv u;
   						meshop.setmapvert mesh 1 vi [u,vv,0.0];
   						vi += 1;
   					)
   					if cornersegs then			-- start row of corner tverts
   					(
   						v = vcoords[i] + size/tlen;	
   						for j = 1 to npsegs_p1 do
   						(
   							vv = v;
   							u = ucoords[j];
   							if flipUV then swap vv u;
   							meshop.setmapvert mesh 1 vi [u,vv,0.0];
   							vi += 1;
   						)
   					)	
   					for k = 1 to segcounts[i] - 1 do -- side subdivisions tvert rows
   					(	
   						v = lerp vcoords[i] vcoords[i+1] (k as float/segcounts[i] as float);
   						for j = 1 to npsegs_p1 do
   						(
   							vv = v; -- restore v every loop
   							u = ucoords[j];
   							if flipUV then swap vv u;
   							meshop.setmapvert mesh 1 vi [u,vv,0.0];
   							vi += 1;
   						)
   					)	
   					if cornersegs then		-- end row of corner tverts
   					(
   						v = vcoords[i+1] - size/tlen;
   						for j = 1 to npsegs_p1 do
   						(
   							vv = v;
   							u = ucoords[j];
   							if flipUV then swap vv u;
   							meshop.setmapvert mesh 1 vi [u,vv,0.0];
   							vi += 1;
   						)
   					)
   				)
   				for j = 1 to npsegs_p1 do -- the last set of verts	
   				(
   					u = ucoords[j];
   					v = 1.0;
   					if flipUV then swap v u;
   					meshop.setmapvert mesh 1 vi [u,v,0.0];
   					vi += 1;
   				)
   -- mapping faces	
   				fi = 1;	
   				si_offset = 0;
   				ei_offset = npsegs_p1;
   				for i = 1 to nlsegs do
   				(	
   					for j = 1 to npsegs  do
   					(
   						s1 = si_offset + j;
   						e1 = ei_offset + j;
   						s2 = s1 + 1;
   						e2 = e1 + 1;
   						meshop.setMapFace mesh 1 fi [e1,e2,s1]
   						fi += 1;
   						meshop.setMapFace mesh 1 fi [s2,s1,e2]
   						fi += 1;
   					)
   					si_offset += npsegs_p1;
   					ei_offset += npsegs_p1;
   				)	
   			)				
   			else -- create mapping base on xy positions
   			(
   				ntverts = 4 * (nlsegs + 1) * npsegs_p1;
   				meshop.setMapSupport mesh 1 true;
   				meshop.setNumMapVerts mesh 1 ntverts;
   				meshop.setNumMapFaces mesh 1 nfaces;
   				
   				ucoords = GetUCoords();
   				w_vcoords = GetVCoords npsegs true;
   				h_vcoords = GetVCoords npsegs false;
   				
   				vi = 1;
   				for i = 1 to 4 do
   				(	
   					for j = 1 to npsegs_p1 do -- initial row
   					(	
   						u = ucoords[j];
   						if i == 1 or i == 3 then
   							v =  0.5 + w_vcoords[j];
   						else
   							v =  0.5 + h_vcoords[j];
   						if flipUV then swap v u;
   						
   						meshop.setmapvert mesh 1 vi [u,v,0.0];
   						vi += 1;
   					)
   					if cornersegs then
   					(
   						if i == 1 or i == 3 then
   							v =  0.5 + w_vcoords[npsegs_p1];
   						else
   							v =  0.5 + h_vcoords[npsegs_p1];
   					
   						for j = 1 to npsegs_p1 do
   						(	
   							vv = v; -- restore vcoord for each loop
   							u = ucoords[j];
   							if flipUV then swap vv u;
   							meshop.setmapvert mesh 1 vi [u,vv,0.0];
   							vi += 1;
   						)
   					)	
   					for k = 1 to segcounts[i] - 1 do -- handle the length wize subdivision	
   					(
   						if i == 1 or i == 3 then
   							v =  0.5 - lerp -w_vcoords[npsegs_p1] w_vcoords[npsegs_p1] (k as float/segcounts[i] as float);
   						else
   							v =  0.5 - lerp -h_vcoords[npsegs_p1] h_vcoords[npsegs_p1] (k as float/segcounts[i] as float);
   					
   						for j = 1 to npsegs_p1 do
   						(	
   							vv = v; -- restore vcoord for each loop
   							u = ucoords[j];
   							if flipUV then swap vv u;
   							meshop.setmapvert mesh 1 vi [u,vv,0.0];
   							vi += 1;
   						)
   					)	
   					if cornersegs then
   					(
   						if i == 1 or i == 3 then
   							v =  0.5 - w_vcoords[npsegs_p1];
   						else
   							v =  0.5 - h_vcoords[npsegs_p1];
   					
   						for j = 1 to npsegs_p1 do
   						(	
   							vv = v; -- restore vcoord for each loop
   							u = ucoords[j];
   							if flipUV then swap vv u;
   							meshop.setmapvert mesh 1 vi [u,vv,0.0];
   							vi += 1;
   						)
   					)			
   					for j = 1 to npsegs_p1 do -- end row	
   					(	
   						u = ucoords[j];
   						if i == 1 or i == 3 then
   							v = 0.5 - w_vcoords[j];
   						else
   							v = 0.5 - h_vcoords[j];
   						if flipUV then swap v u;
   						meshop.setmapvert mesh 1 vi [u,v,0.0];
   						vi += 1;
   					)
   				)	
   -- create the faces, every face generating routine is slightly different !!! :? 				
   				fi = 1;	
   				si_offset = 0;
   				ei_offset = npsegs_p1;
   				for i = 1 to 4 do
   				(	
   					segs = segcounts[i];
   					if cornersegs then segs += 2;
   					for k = 1 to segs do
   					(	
   						for j = 1 to npsegs  do
   						(
   							s1 = si_offset + j;
   							e1 = ei_offset + j;
   							s2 = s1 + 1;
   							e2 = e1 + 1;
   							meshop.setMapFace mesh 1 fi [e1,e2,s1];
   							fi += 1;
   							meshop.setMapFace mesh 1 fi [s2,s1,e2];
   							fi += 1;
   						)
   						si_offset += npsegs_p1;
   						ei_offset += npsegs_p1;
   					)	
   					si_offset += npsegs_p1;
   					ei_offset += npsegs_p1;
   				)
   			)		
   		)	
   		if picQuad then   -- add our picture quad with sub divisions
   		(
   			msh = TriMesh();
   			setMesh msh length:height  width:width  lengthsegs:hsegs  widthsegs:wsegs;
   			for v = 1 to msh.numverts do  -- offset so its center on the origin
   				setvert msh v ((getvert msh v) - [w,h,-curvedata[curvedata.count].y * zscale]); 
   			if mapCoords then
   				meshop.defaultMapFaces msh 1; -- add a normalized planar map
   			mesh += msh;	-- boolean is nice as it even welds the verts
			delete msh;	   
   		)	
   		update mesh;
   	)
   	on update do doBuildMesh();
   	on buildmesh do doBuildMesh();
   )

still think it’s limited ?

added

delete msh;  

seemed to cause issues with gc() without it.

2 Replies
(@denist)
Joined: 11 months ago

Posts: 0

very nice plug-in! but…
for better performance usually making a simpleObject script is a good practice to realize ‘full’ and ‘light’ mesh build functions. the full version builds both topology and geometry, but the light version just only geometry. for example: why do we need fully rebuild a mesh when the object’s size (width, height, etc.) changes? some parameter change needs only the mesh vertices move, not a mesh creation.
this concept helps get much better performance and, which is most important sometimes, save a memory.

(@denist)
Joined: 11 months ago

Posts: 0

and it will be super cool to add round corners as an option

Thanks for the help Klunk, I’ve felt pretty thick headed trying to pick up on all this stuff and realizing I was going about it all wrong. I didn’t understand that the plugin worked by recreating it each time. I realize now I don’t even need most of the properties I was looking for. Thanks for the help and the patience, I’m finally wrapping my head around the best methods for creating an object using code.

i don’t tend to worry too much about performance as I tend to use scripted plugins as quick turn around prototyping for the sdk that’s why I tend to use the raw mesh creation methods as they port easily back and forth between the sdk and mxs. Using stuff like max primitives may have performance and memory benefits but it doesn’t work well in the sdk. And yes there probably a bit of feature overload but it’s partly an educational device to

Denis that Curve Control snippet I ripped off is so damn useful, lots of applications for low resolution shapes and profiles for quick object generation.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

i have unfinished version with bezier-points support. it’s automatically calculates the number of steps (like optimized steps spline interpolation in max) and ‘smooth’ areas.

and it will be super cool to add round corners as an option

variable sided picture frame/ or a lathed version would probably be best approach or a specialized compound lofted profile thingy.

using the maxobject/bezier curve data parameter is a nice touch as it mirrors the curvecontrol curves nicely but I couldn’t get it to create an instance directly from mxs using that method so i use the simplfied point3 tab. Do you have a fix for that ?

It’s also a shame there’s no bezier curve parameter block type suppose you could squeeze it into a matrix3 tab

i’ve posted on this forum how to convert curvecontrol curve to bezierfloat controller and back. so… in my tools i store a bezier controller as maxobject type parameter

Page 1 / 2