[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.
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.