--New Script by Jonah Peele, it's been a while (c) 5/27/2020 -- plan of action: -- User has the box object selected -- User runs script -- User input -> pick the target object -- target object is cloned -- cloned object has a FFD_box modeifier added -- FFDbox modifier gets animatable points enabled -- points are moved to match the box object vertices organized from botom to top, screen space depth to box object y depth -- ^-- ran into issue with this logic. ffd has interior points frame object does not. will switch to skin wrap instead (edit: skinwrap sucks, just accept that there can't be interior ffd cp's macroscript polyLattice3 category:"jpScripts" tooltip:"polyLattice3" ( --Huge thanks to ZeBoxx2 for these functions, they certainly get their fair share of usage -- convert FFD local (normalized) coordinates to world coordinates -- obj = object containing the modifier -- ffdmod = the actual modifier -- pos = the local coordinate fn ffdLocalToWorldPos obj ffdmod pos = ( objTM = obj.objecttransform modTM = (getModContextTM obj ffdmod) * ffdmod.lattice_transform.value modBBMin = getModContextBBoxMin obj ffdmod modBBMax = getModContextBBoxMax obj ffdmod if modBBMax.z == 0 then modBBMax.z = .001 -- to avoid divide by zero some where (modBBMin + (pos * (modBBMax-modBBMin)) * (inverse modTM) * objTM) ) -- convert world coordinates to FFD local (normalized) coordinates -- obj = object containing the modifier -- ffdmod = the actual modifier -- pos = the world coordinate fn ffdWorldToLocalPos obj ffdmod pos = ( objTM = obj.objecttransform modTM = (getModContextTM obj ffdmod) * ffdmod.lattice_transform.value modBBMin = getModContextBBoxMin obj ffdmod modBBMax = getModContextBBoxMax obj ffdmod if modBBMax.z == 0 then modBBMax.z = .001 -- to avoid divide by zero some where (pos - modBBMin) * (inverse objTM) * inverse modTM / (modBBMax-modBBMin) ) -- get an FFD mod's control point's world coordinate -- obj = object containing the modifier -- ffdmod = the actual modifier -- i = control point index fn ffdGetWorldPos obj ffdmod i = ( cp = ffdmod["Master"][i] return ffdLocalToWorldPos obj ffdmod cp.value ) -- set an FFD mod's control point's world coordinate -- obj = object containing the modifier -- ffdmod = the actual modifier -- i = control point index -- pos = new world position fn ffdSetWorldPos obj ffdmod i pos = ( completeredraw() cp = ffdmod["Master"][i] cp.value = ffdWorldToLocalPos obj ffdmod pos ) fn animateFFDSpaceWarp o = -- credit ZeBoxx2 -- http://forums.cgsociety.org/showthread.php?f=98&t=720508&page=1&pp=15 ( local WM_USER = 1024 local CC_SPINNER_CHANGE = WM_USER + 600 select o max modify mode subObjectLevel = 1 max select all max move max tti local desktopHWND = windows.getDesktopHWND() local desktopChildren = windows.getChildrenHWND desktopHWND local tti for child in desktopChildren do ( if (child[5] == "Move Transform Type-In") then ( tti = child exit ) ) if (tti == undefined) then ( return false ) local ttiHWND = tti[1] local ttiChildren = windows.getChildrenHWND ttiHWND local anySpinner for child in ttiChildren do ( if (child[4] == "SpinnerControl") then ( anySpinner = child; exit ) ) if (anySpinner == undefined) then ( return false ) local anySpinnerHWND = anySpinner[1] local anySpinnerDlgID = uiaccessor.GetWindowResourceID anySpinnerHWND local anySpinnerParent = uiaccessor.getparentwindow anySpinnerHWND animButtonState = true local oldSliderTime = sliderTime sliderTime = 1 windows.sendmessage anySpinnerParent CC_SPINNER_CHANGE anySpinnerDlgID anySpinnerHWND sliderTime = oldSliderTime animButtonState = false UIAccessor.CloseDialog ttiHWND OK ) -- end of ZeBoxx2 helper functions fn GetNodeFacesByMatID node = -- credit PolyTools3D --> https://forums.cgsociety.org/t/get-faces-from-material-id/1686018/2 ( getfacematid = polyop.getfacematid r=#(); ids=#{} for f = 1 to node.numfaces do ( id = getfacematid node f if not ids[id] then r[id]=#{f} else r[id][f]=true; ids[id]=true ) for j in ids collect #(j, r[j]) ) fn MatIDsharedEdges obj id1 id2 = ( facedata = GetNodeFacesByMatID obj set1 = polyop.getedgesusingface obj facedata[id1][2] set2 = polyop.getedgesusingface obj facedata[id2][2] set1 * set2 ) --todo: find original author for credit fn AlignPivotTo Obj Trgt = ( -- Get matrix from object if classOf Trgt != matrix3 then Trgt = Trgt.transform -- Store child transforms local ChldTms = in coordSys Trgt ( for Chld in Obj.children collect Chld.transform ) -- Current offset transform matrix local TmScale = scaleMatrix Obj.objectOffsetScale local TmRot = Obj.objectOffsetRot as matrix3 local TmPos = transMatrix Obj.objectOffsetPos local TmOffset = TmScale * TmRot * TmPos -- New offset transform matrix TmOffset *= obj.transform * inverse Trgt -- Apply matrix Obj.transform = Trgt -- Restore offsets Obj.objectOffsetPos = TmOffset.translation Obj.objectOffsetRot = TmOffset.rotation Obj.objectOffsetScale = TmOffset.scale -- Restore child transforms for i = 1 to Obj.children.count do Obj.children[i].transform = ChldTms[i] * inverse Trgt * Obj.transform ) fn sortvertsbyscreenX v1 v2 obj:$ = ( s1 = gw.TransPoint (polyop.getVert obj v1) s2 = gw.TransPoint (polyop.getVert obj v2) x = s1[1] - s2[1] case of ( (x > 0.): 1 (x < 0.): -1 default:0 ) ) fn sortboxverts obj = ( select obj subobjectlevel =1 facedata = GetNodeFacesByMatID obj -- testorder = #() orderedverts = #() vertset = #() allverts = #{1..obj.numverts} sortedverts = #{} bottomverts = (polyop.getvertsusingface obj facedata[2][2]) append vertset bottomverts sortedverts = bottomverts obj.EditablePoly.SetSelection #Vertex bottomverts while ( (allverts as array).count > (sortedverts as array).count) do ( obj.growselection selLevel:#vertex newverts = ((obj.getselection #vertex) - sortedverts) obj.setselection #vertex newverts sortedverts = sortedverts + newverts append vertset newverts ) for vset in vertset do ( backverts = polyop.getvertsusingface obj facedata[6][2] frontverts = polyop.getvertsusingface obj facedata[5][2] backverts = (backverts * vset) as array frontverts = (frontverts * vset) as array qsort backverts sortvertsbyscreenX obj:obj qsort frontverts sortvertsbyscreenX obj:obj -- append testorder frontverts -- append testorder backverts join orderedverts frontverts join orderedverts backverts ) orderedverts ) fn polyLat3 frame:selection[1] obj:(pickObject prompt:"pick PL3 target object" rubberBand:selection[1].pos rubberBandColor:yellow) = ( --get initial face data --face node data for initial box: 1=top, 2=bottom, 3=left, 4=right, 5=front, 6=back facedata = GetNodeFacesByMatID frame topface = (facedata[1][2] as array) bottomface = (facedata[2][2] as array) --get the face edges bottomback = MatIDsharedEdges frame 2 6 bottomleft = MatIDsharedEdges frame 2 3 bottomright = MatIDsharedEdges frame 2 4 bottomfront = MatIDsharedEdges frame 2 5 topback = MatIDsharedEdges frame 1 6 topleft = MatIDsharedEdges frame 1 3 topright = MatIDsharedEdges frame 1 4 topfront = MatIDsharedEdges frame 1 5 --we'll assume the frame has the correct set up, use one side to check the eventual ffd height segs sideseg = MatIDsharedEdges frame 3 5 --ATTEMPT 3: limit the ffdbox length sength segments to 2 --clone and create ffd target -- target = (reference obj) select obj maxOps.CloneNodes (selection as array) cloneType:#reference newNodes:&newArr target = newArr[1] completeredraw() ffdmod = FFDBox () setDimensions ffdmod [(bottomfront as array).count+1, (bottomleft as array).count+1, (sideseg as array).count+1] addmodifier target ffdmod animateAll ffdmod completeredraw() sbv = sortboxverts frame --< this is the verts sorted by z value, next we need to sort the verts by front/back (compare to material id), and sort the vets by screen X value. for i = 1 to sbv.count do ( newPos = (polyop.getVert frame sbv[i]) ffdSetWorldPos target ffdmod i newPos ) target.pivot = (target.max + target.min)/2 -- FAILED ATTEMPT 2 BELOW: skin wrap doesn't deform well enough -- tempframe = bboxTobox obj -- tempframe.lengthsegs = (bottomleft as array).count -- tempframe.widthsegs = (bottomfront as array).count -- tempframe.heightsegs = (sideseg as array).count -- FAILED ATTEMPT 1 BELOW: use skin wrap since frame object doesn't have interior points -- --clone and create ffd target -- target = (reference obj) -- ffdmod = FFDBox () -- setDimensions ffdmod [(bottomfront as array).count+1, (bottomleft as array).count+1, (sideseg as array).count+1] -- addmodifier target ffdmod -- animateAll ffdmod -- -- at this point we have the frame object and the cloned ffdboxed object, but the verts are un ordered. -- --first attempt will be to qsort the verts their position magnitude for ) on isVisible return subObjectLevel == 0 and classof selection[1] == Editable_Poly and selection.count == 1 --show only when EPoly is selected and in Face Sub_Object mode on execute do ( polyLat3() ) )