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