Notifications
Clear all

[Closed] Need Help converting a MAXScript to .NET

some quick tests with the original Denis script where the box has 4 segs in every dimension and the created planes have 4 segs.

sdk create t:31
mxs create t:109
ffd t:9484  

the command panel was docked with redraw disabled

though if you use disableRefMsgs() & enableRefMsgs()
you can get the ffd down to 110.

and if you make the planes 100×100 segs you get

sdk create t:156
mxs create t:26203
ffd t:390

and for 1×1 segs you get

sdk create t:16
mxs create t:62
ffd t:109

the code I’ve been running…

fn lerp x y s = (x + s * (y - x))
 
 fn mxssetedgevisflags m face flags =
 (	
 	setEdgeVis m  face 1 flags[1];
 	setEdgeVis m  face 2 flags[2];
 	setEdgeVis m  face 3 flags[3];
 )
 	
 fn MakePlaneMesh bottom_left bottom_right top_right top_left width_segs:4  length_segs:4 =
 (
 	local wverts = width_segs + 1;
 	local lverts = length_segs + 1; 
 	local nverts = wverts * lverts;
 	local nfaces = length_segs * width_segs * 2;
 	local lscalar = 1.0/length_segs;
 	local wscalar = 1.0/width_segs;
 	local len_segs_m1 = length_segs - 1;
 	local wid_segs_m1 = width_segs - 1;
 	
 -- build the coordsys	
 	
 	local xaxis = normalize (bottom_right - bottom_left);
 	local temp =  bottom_left - top_left;
 	local zaxis = normalize (cross temp xaxis);
 	local yaxis = normalize (cross zaxis xaxis );
 	local trans = (bottom_right + bottom_left + top_left + top_right) * 0.25;
 	local tm = (matrix3 xaxis yaxis zaxis trans);
 	
 -- create an empty mesh
 	
 	msh = mesh numverts:nverts numfaces:nfaces;
 	
 -- setup the default mapping channel	
 	
 	meshop.setMapSupport msh 1 true;
 	meshop.setNumMapVerts msh 1 nverts;
 	meshop.setNumMapFaces msh 1 nfaces; 
 	
 -- set the transform	
 	
 	msh.transform = tm;
 	
 -- generate the verts		
 	
 	local vi = 1;	-- vert index
 	for i = 1 to lverts do
 	(
 		local iscale = (i-1) * lscalar;
 		
 		local vert_start = lerp bottom_left top_left iscale;
 		local vert_end = lerp bottom_right top_right iscale;	
 		
 		local tvert_start = [0,lerp 0 1 iscale,0];
		local tvert_end = [1,lerp 0 1 iscale,0];
 		
 		for j = 1 to wverts do
 		(
 			local jscale = (j-1) * wscalar;
 			setVert msh vi (lerp vert_start vert_end jscale);
 			meshop.setMapVert msh 1 vi (lerp tvert_start tvert_end jscale);	
 			vi += 1;
 		)
 	)
 	
 -- generate the faces	
 	
 	local fi = 1; -- face index
 	for i = 0 to len_segs_m1 do
 	(
 		j = i * wverts + 1;	-- compute the starting vert index
 		for k = 0 to wid_segs_m1 do
 		(
 -- quad vert indices			
 			
 			local na = j + k;
 			local nb = na + wverts;
 			local nc = nb + 1;
 			local nd = na + 1;
 			
 -- create first quad face			
 			
 			setface msh fi [nb,na,nc];
 			mxssetedgevisflags msh fi #{1,3};
 			meshop.setMapFace msh 1 fi [nb,na,nc];
 			fi += 1;
 			
 -- create second quad face			
 			
 			setface msh fi [nc,na,nd];
 			mxssetedgevisflags msh fi #{2,3};
 			meshop.setMapFace msh 1 fi [nc,na,nd];
 			fi += 1;
 		)
 	)
 	update msh;
 	msh;
 )	
   
 fn ToggleMaxRedraw arg =
 (
 	if arg == 1 then
 	(
 		cui.CommandPanelOpen = true; 
 		enableSceneRedraw();
 		enableRefMsgs();
 	) 
 	else 
 	(
 		disableSceneRedraw();
 		disableRefMsgs();
 	)	
 	WM_SETREDRAW = 0xB;
 	hwnd = (windows.getChildHWND #max "Command Panel");
 	
 -- is the command panel floating ?	
 	
 	if hwnd == undefined then -- this doesn't really work because when floating I think max/windows turns redraw back on somewhere 
 		hwnd = (windows.getChildHWND (windows.getDesktopHWND()) "Command Panel"); -- works with disableRefMsgs() though ! :)
 		
 -- if something didn't go wrong !
 			
 	if 	hwnd != undefined then	
 		windows.sendmessage (hwnd[1]) WM_SETREDRAW arg 0;
 )
 
 fn worldPointToFFD p ffd pos = 
 (
 	otm = p.objecttransform
 	mtm = (getModContextTM p ffd)*ffd.lattice_transform.value
 	bmin = getModContextBBoxMin p ffd
 	bmax = getModContextBBoxMax p ffd
 
 	v = (pos * (inverse otm) * mtm - bmin)/(bmax - bmin)
 	v.z = 0
 	v
 )
 
 fn alignPlaneToQuad node:selection[1] face:undefined planeDimension:[1,1] = if iskindof node Editable_Poly do 
 (
 	if face == undefined and node.selectedfaces.count > 0 do face = node.selectedfaces[1].index 
 	if face != undefined and (vv = polyop.getfaceverts node face).count == 4 do
 	(
 		v1 = polyop.getvert node vv[1]
 		v2 = polyop.getvert node vv[2]
 		v3 = polyop.getvert node vv[3]
 		v4 = polyop.getvert node vv[4]
 
 		front = normalize (v1-v2)
 		side = normalize (v2-v3)
 		up = polyop.getfacenormal node face
 		center = polyop.getfacecenter node face
 
 		tm = orthogonalize (matrix3 front side up center)
 
 		tm = pretranslate tm [0.5, 0.5, 0]
 		p = plane width:1 length:1 widthsegs:planeDimension.x lengthsegs:planeDimension.y transform:tm
 		ffd = FFDBox deformType:1 tension:25.0 continuity:0.0
 		setDimensions ffd [2,2,2]
 		animateall ffd
 		addmodifier p ffd
 		modpanel.setcurrentobject ffd
 		ffd.control_point_1 = ffd.control_point_5 = worldPointToFFD p ffd v3
 		ffd.control_point_2 = ffd.control_point_6 = worldPointToFFD p ffd v4
 		ffd.control_point_3 = ffd.control_point_7 = worldPointToFFD p ffd v2
 		ffd.control_point_4 = ffd.control_point_8 = worldPointToFFD p ffd v1
 		p
 	)
 )
   
 fn alignPlaneToQuad2 node:selection[1] face:undefined planeDimension:[1,1] = if iskindof node Editable_Poly do 
 (
 	if face == undefined and node.selectedfaces.count > 0 do face = node.selectedfaces[1].index 
 	if face != undefined and (vv = polyop.getfaceverts node face).count == 4 do
 	(
 		v1 = polyop.getvert node vv[1]
 		v2 = polyop.getvert node vv[2]
 		v3 = polyop.getvert node vv[3]
 		v4 = polyop.getvert node vv[4]
 		MakePlaneMesh v1 v2 v3 v4 width_segs:planeDimension.x  length_segs:planeDimension.y;
 	)
 )
   
 fn alignPlaneToQuad3 node:selection[1] face:undefined planeDimension:[1,1] = if iskindof node Editable_Poly do 
 (
 	if face == undefined and node.selectedfaces.count > 0 do face = node.selectedfaces[1].index 
 	if face != undefined and (vv = polyop.getfaceverts node face).count == 4 do
 	(
 		v1 = polyop.getvert node vv[1]
 		v2 = polyop.getvert node vv[2]
 		v3 = polyop.getvert node vv[3]
 		v4 = polyop.getvert node vv[4]
 		planeFromCornerPoints v1 v2 v3 v4 width_segs:planeDimension.x  length_segs:planeDimension.y;
 	)
 ) 
 
 ( 
 	clearlistener();
 	delete objects;
 	subdiv = 5;
 	b = box length:64 width:64 height:32  lengthsegs:subdiv widthsegs:subdiv heightsegs:subdiv;
 	addmodifier b (taper amount:-0.7);
 	convertToPoly b;
 	update b;
 	rotate b (angleaxis -32.2873 [-0.803173,0.16373,-0.572805]) --need to test something
 
 	max modify mode;
 /*    (
 		t = timestamp();
 		for f in b.faces do alignPlaneToQuad3 node:b face:f.index planeDimension:[1,1];
 		format "sdk create t:% ms
" (timestamp() - t) ;
 	)*/
 	(
 		t = timestamp();
 		for f in b.faces do alignPlaneToQuad2 node:b face:f.index planeDimension:[1,1];
 		format "mxs create t:% ms
" (timestamp() - t);
 	)
 	(
 		t = timestamp();
 		ToggleMaxRedraw 0; 
 		for f in b.faces do 
 		(
 			p = alignPlaneToQuad node:b face:f.index planeDimension:[1,1];
 			convertToMesh p; -- if you need them as meshes this can be quite a hit
 		)	
 		ToggleMaxRedraw 1;	
 		format "ffd t:% ms
" (timestamp() - t) 
 	)
 	
 )   

Klunk… this code is extremely helpful. I had not yet run across the [b]disableRefMsgs/b/enableRefMsgs[font=Courier New]size=2 [font=Verdana]functions in the past.[/font][/size][/font]

I noticed that, when using the sample code the FFD route generates meshes that are collapsed to a single point.

In any event, I will experiment a little with these new functions and test.

Thank you.

can’t comment on the ffd code, just a direct copy from the link you posted. Also the disableRefMsgs()/enableRefMsgs() come with a health warning USE WITH EXTREME CAUTION!!! or so it says in the help but that just describes max in general.

I saw that. It would be nice to know what to be careful about…

mxs is slow but not very slow… here is a sample where i’m deforming a tessellated triangle object and recalculate all vert position using point-to-bary – bary-to-point methods.
as you can see for >30K verts it take just <0.5 sec. of course with c++ it will take probably ~0.1 sec

fn pointToBary p a b c = 
(
	vw = cross (c - a) (p - a)
	uw = cross (b - a) (p - a)
	uv = cross (b - a) (c - a)

	denom = length uv
	r = (length vw)/denom
	t = (length uw)/denom
	[1-r-t,r,t]
)
fn baryToPoint p a b c = (p.x*a + p.y*b + p.z*c)

delete objects

A = [0,0,0]
B = [0,100,0]
C = [100,100,0]
p = mesh name:"p" vertices:#(A, B, C) faces:#([1,2,3])

addmodifier p (createinstance tessellate tension:0 faceType:0 type:0 iterations:7) 
converttomesh p
gc()
with redraw off
(
	-- seed 0
	nA = A + random [-40,-40,0] [40,40,0] 
	nB = B + random [-40,-40,0] [40,40,0] 
	nC = C + random [-40,-40,0] [40,40,0] 

	t1 = timestamp()
	for v=1 to p.numverts do
	(
		n = pointToBary (getvert p v) A B C
		setvert p v (baryToPoint n nA nB nC)
	)
	update p
	t2 = timestamp()
	format "count:% time:%
" p.numverts (t2-t1)

	A = copy nA
	B = copy nB
	C = copy nC
	ok
)

I saw that. It would be nice to know what to be careful about…

what to be careful about, these are the reference messages sent between a referenceMaker and a referenceTarget and as you can see nearly every thing in max hangs off referenceTarget !

Thanks Denis and Klunk. The input is valuable.

The last couple of days of experimentation have proven to me that I really don’t need to consider C++/C# for the functions I’m trying to optimize at this moment.

The disableRefMsgs()/enableRefMsgs() helped significantly in a couple of the functions I wanted to speed up (namely when using any modifiers that have the misfortune of requiring the Modify Tab to be open). The other functions have seen even more improvement by using better search algorithms when using large arrays of data. I will share some of this input when I am done with my current project.

By taking some steps at going through the code with a fine comb and the help of a comrade who taught me better searching methods, I’ve been able to reduce some slow functions by orders of magnitude.

Page 2 / 2