this should get you started.....
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;
picplane 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 Plane." 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 picplane 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();
)