Files
2025-09-29 00:52:08 +02:00

819 lines
24 KiB
Plaintext
Executable File

-- Utility-functions for use by SkinDataTransfer tool:
struct RsSkinDataTransfer
(
-- Supplies list of verts adjacent to vertex vertNum, ordered anticlockwise
fn adjacentVerts obj vertNum listStartVert: startEdgeNum:1 =
(
--local DEBUGDATA = #(#ADJACENTVERTS, obj, vertNum, listStartVert, startEdgeNum)
-- Get the faces adjacent to vertNum:
local adjFaceNums = polyop.getFacesUsingVert obj vertNum
local adjFaceVertsList = #()
-- Get these faces' verts adjacent to vertNum, in anticlockwise order
local faceVerts, faceVertCount, initialVertIndex, faceVerts, firstAdj, lastAdj, insertAt
for faceNum in adjFaceNums collect
(
faceVerts = polyop.getFaceVerts obj faceNum
faceVertCount = faceVerts.count
initialVertIndex = findItem faceVerts vertNum
case initialVertIndex of
(
1:
(
firstAdj = faceVerts[2]
lastAdj = faceVerts[faceVertCount]
)
faceVertCount:
(
firstAdj = faceVerts[1]
lastAdj = faceVerts[faceVertCount - 1]
)
default:
(
firstAdj = faceVerts[initialVertIndex + 1]
lastAdj = faceVerts[initialVertIndex - 1]
)
)
faceVerts = #(#(firstAdj, lastAdj))
append adjFaceVertsList faceVerts
)
-- Order face-list
local vertList, firstVert, lastVert, checkList
local listRemoved = #{}
listRemoved.count = adjFaceVertsList.count
local changed = true
while changed do
(
changed = false
for n = 1 to adjFaceVertsList.count where not listRemoved[n] do
(
searchVertList = adjFaceVertsList[n]
lastVert = searchVertList[searchVertList.count][2]
for m = 1 to adjFaceVertsList.count where (m != n) and not listRemoved[m] do
(
checkList = adjFaceVertsList[m]
if (checkList[1][1] == lastVert) do
(
join searchVertList checkList
adjFaceVertsList[m] = undefined
listRemoved[m] = true
changed = true
)
)
)
)
-- Remove empty items left by sorting process:
adjFaceVertsList = for item in adjFaceVertsList where (item != undefined) collect item
if adjFaceVertsList.count > 1 do
(
format "Open?!?\t%\n" (adjFaceVertsList as string)
)
-- Output the verts adjacent to vertNum in clockwise order, using the first vert on each adjacent face:
outVal = for item in adjFaceVertsList[1] collect item[1]
-- Complete open vert-lists:
adjFaceVertsList = adjFaceVertsList[1]
local lastVert = adjFaceVertsList[adjFaceVertsList.count][2]
if (outVal[1] != lastVert) do
(
append outVal lastVert
)
-- Find requested start of list:
if (listStartVert != unsupplied) do
(
startEdgeNum = findItem outVal listStartVert
)
-- If startEdgeNum is used, rotate edge-order accordingly:
if (startEdgeNum > 1) do
(
local rotatedVerts = (for n = startEdgeNum to outVal.count collect outVal[n])
join rotatedVerts (for n = 1 to (startEdgeNum - 1) collect outVal[n])
outVal = rotatedVerts
)
--format "Rotated:%\n" (outVal as string)
outVal
),
fn expandTree obj item prevVert: doneVerts:#{} vertInfo:#() expandAll:false =
(
local vertNum = item[1]
case classOf item[2] of
(
UndefinedClass:
(
if expandAll or (not doneVerts[vertNum]) do
(
doneVerts[vertNum] = true
item[2] = for adjVert in (adjacentVerts obj vertNum listStartVert:prevVert) collect #(adjVert, undefined, undefined)
vertInfo[vertNum] = item[2]
)
)
Array:
(
for subItem in item[2] do
(
doneVerts[vertNum] = true
expandTree obj subItem prevVert:vertNum doneVerts:doneVerts vertInfo:vertInfo expandAll:expandAll
)
)
)
),
fn treeHash item =
(
if item[2][1][2] == undefined then
(
item[2].count
)
else
(
getHashValue (for subItem in item[2] collect (treeHash subItem)) 0
)
),
fn getUniquePatternHashLevel obj startVert maxPatternDepth:5 =
(
-- Increase side of adjacency-tree until only one rotation produces a unique hash
local adjVertNums = adjacentVerts obj startVert
local rootAdjacencies = for adjVert in adjVertNums collect #(adjVert, undefined)
local rootEdgeCount = rootAdjacencies.count
local adjacencyTree = #(startVert, rootAdjacencies)
local altTrees = for n = 2 to rootEdgeCount collect
(
local rotatedEdges = (for m = n to rootEdgeCount collect rootAdjacencies[m])
join rotatedEdges (for m = 1 to (n - 1) collect rootAdjacencies[m])
#(startVert, rotatedEdges)
)
local patternDepth = 0
local lastDoneVertsCount
local doneVerts = #{}
-- Expand the tree out until its first tree-topology is unique, (to avoid upside-down matches or similar),
-- all verts have been accounted for (this will happen for radially-symmetrical objects),
-- or the maxPatternDepth has been reached (where the selected vert is in a radially-symmetrical region)
local patternFound, noMatch, patternHash, altHash
while
(
(patternDepth < maxPatternDepth) and
(lastDoneVertsCount != doneVerts.numberSet) and
(patternFound == undefined)
)
do
(
patternDepth += 1
lastDoneVertsCount = doneVerts.numberSet
expandTree obj adjacencyTree doneVerts:doneVerts expandAll:true
patternHash = treeHash adjacencyTree
--format "% hash 0: %\n" obj.name (patternHash as string)
noMatch = true
local treeNum = 0
for altTree in altTrees while noMatch do
(
treeNum += 1
altHash = treeHash altTree
--format "% hash %: %\n" obj.name treeNum (altHash as string)
noMatch = (altHash != patternHash)
if not noMatch do
(
--format "patternDepth %: treeNum % matched: %\n" patternDepth treeNum patternHash
)
)
if noMatch do
(
patternFound = patternHash
)
)
-- If matching-area has radially-symmetrical topology, supply the topmost vert to provide an initial edge to work from
-- This will most likely be the top vert of an eyeball, so should be the same on both models.
local topVertNum = if noMatch then unsupplied else
(
local adjVertPosZs = for adjVert in adjVertNums collect (polyop.getVert obj adjVert).z
local upperVertNum = 1
local upperVertZ = adjVertPosZs[1]
for n = 2 to adjVertNums.count do
(
if (adjVertPosZs[n] > upperVertZ) do
(
upperVertZ = adjVertPosZs[n]
upperVertNum = n
)
)
upperVertNum
)
struct RSskinXferPatternHash (patternDepth, patternHash, topVertNum)
return RSskinXferPatternHash patternDepth patternHash topVertNum
),
fn findPatternTreeMatch obj startVert patternHashData =
(
local DEBUGDATA = #(#findPatternTreeMatch, obj, patternHashData)
local patternDepth = patternHashData.patternDepth
local patternHash = patternHashData.patternHash
local sourceTopVert = patternHashData.topVertNum
local adjVertNums = adjacentVerts obj startVert
local rootAdjacencies = for adjVert in adjVertNums collect #(adjVert, undefined)
local rootEdgeCount = rootAdjacencies.count
local vertTrees = #(#(startVert, rootAdjacencies))
for n = 1 to patternDepth do
(
expandTree obj vertTrees[1] expandAll:true
)
for n = 2 to rootEdgeCount do
(
local rotatedEdges = (for m = n to rootEdgeCount collect rootAdjacencies[m])
join rotatedEdges (for m = 1 to (n - 1) collect rootAdjacencies[m])
append vertTrees #(startVert, rotatedEdges)
)
local foundTreeNum
for n = 1 to vertTrees.count while (foundTreeNum == undefined) do
(
if ((treeHash vertTrees[n]) == patternHash) do
(
foundTreeNum = n
)
)
format "TREENUM: %\n" foundTreeNum
-- If pattern matches, but has radially-symmetrical topology, a Top Vert will have been provided to match with:
if (foundTreeNum != undefined) and (sourceTopVert != unsupplied) do
(
-- print "Toppy time!"
local adjVertPosZs = for adjVert in adjVertNums collect (polyop.getVert obj adjVert).z
local upperVertZ = adjVertPosZs[1]
local topVertNum = 1
for n = 2 to adjVertNums.count do
(
if (adjVertPosZs[n] > upperVertZ) do
(
upperVertZ = adjVertPosZs[n]
topVertNum = n
)
format "Top vert: %: %\n" foundTreeNum adjVertNums[foundTreeNum]
)
-- Use the two meshes' Top Verts to work out which edge-number corresponds with Edge 1 on the source-mesh:
foundTreeNum = (mod (adjVertNums.count + topVertNum - sourceTopVert + 1) adjVertNums.count) as integer
if (foundTreeNum == 0) do (foundTreeNum = adjVertNums.count)
)
-- This number is the edge-number of the target-mesh's pattern-tree that corresponds to the first edge of the source
foundTreeNum
),
-- Provides a list of all verts sharing a mesh-element with "vert"
fn getConnectedVerts obj vert =
(
local vertFaces = polyop.getFacesUsingVert obj vert
local elementFaces = polyop.getElementsUsingFace obj vertFaces
polyop.getVertsUsedOnlyByFaces obj elementFaces
),
fn matchObjVerts baseSourceObj baseTargetObj sourceVert targetVert targetStartEdge:1 debugVertMove:false =
(
struct vertMatchData (sourceVert, targetVert)
local sourceNumVerts = polyop.getNumVerts baseSourceObj
local targetNumVerts = polyop.getNumVerts baseTargetObj
local sourceAdjVerts = adjacentVerts baseSourceObj sourceVert
local targetAdjVerts = adjacentVerts baseTargetObj targetVert startEdgeNum:targetStartEdge
local matchArray = #()
matchArray.count = sourceNumVerts
matchArray[sourceVert] = vertMatchData sourceVert:sourceVert targetVert:targetVert
-- Set up initial matched verts
local vertsMatched = #{sourceVert}
vertsMatched.count = sourceNumVerts
for n = 1 to sourceAdjVerts.count do
(
matchArray[sourceAdjVerts[n]] = vertMatchData sourceVert:sourceAdjVerts[n] targetVert:targetAdjVerts[n]
vertsMatched[sourceAdjVerts[n]] = true
)
local unmatchedVerts
if debugVertMove do
(
for vertNum in vertsMatched do
(
polyop.setVert baseTargetObj matchArray[vertNum].targetVert (polyop.getVert baseSourceObj vertNum) node:baseSourceObj
--polyop.setVertColor baseTargetObj 0 matchArray[vertNum].targetVert green
)
unmatchedVerts = getConnectedVerts baseTargetObj targetVert
)
-- Get edge-lists:
local sourceEdgeVerts = for edgeNum = 1 to (polyop.getNumEdges baseSourceObj) collect
(
(polyop.getEdgeVerts baseSourceObj edgeNum) as bitArray
)
local targetEdgeVerts = for edgeNum = 1 to (polyop.getNumEdges baseTargetObj) collect
(
(polyop.getEdgeVerts baseTargetObj edgeNum) as bitArray
)
local matchedSourceEdges = #{}
local matchedTargetEdges = #{}
local matchedSourceFaces = #{}
local matchedTargetFaces = #{}
local edgeMatches = #()
edgeMatches.count = sourceEdgeVerts.count
-- Function to re-order a face's vert-list, putting two particular verts to the start of the list:
fn edgeOrderFace faceVerts vertA vertB =
(
local firstVert = case of
(
(faceVerts[1] == vertA):
(
if (faceVerts[2] == vertB) then 1 else faceVerts.count
)
(faceVerts[1] == vertB):
(
if (faceVerts[2] == vertA) then 1 else faceVerts.count
)
default:
(
local findA = findItem faceVerts vertA
local findB = findItem faceVerts vertB
if (findA < findB) then findA else findB
)
)
local newList = (for n = firstVert to faceVerts.count collect faceVerts[n])
join newList (for n = 1 to (firstVert - 1) collect faceVerts[n])
newList
)
if not debugVertMove do
(
progressStart "Matching verts:"
)
local maxVertCount = (getConnectedVerts baseSourceObj sourceVert).count
local lastMatched = 0
local notCancelled = true
-- Predefine all variables that'll be reused a lot
local keepLooking, sourceEdge, targetEdge, sourceEdgeFaces, targetEdgeFaces, sourceFaceVertLists, targetFaceVertLists
local searchEdge, sourceVerts, sourceVert, targetVerts, targetVert, targetEdgeNum, newMatchedFaces, checkVert
-- Keep looping until loop stops making changes
while (lastMatched != vertsMatched.numberSet) \
and (notCancelled = progressUpdate (100.0 * vertsMatched.numberSet / maxVertCount)) \
-- for n = 1 to 1
do
(
lastMatched = vertsMatched.numberSet
-- MATCH UP EDGES FOR NEWLY-MATCHED VERTS --
local newMatchedEdges = #{}
newMatchedEdges.count = sourceEdgeVerts.count
-- For unmatched edges...
for sourceEdgeNum = 1 to sourceEdgeVerts.count where not matchedSourceEdges[sourceEdgeNum] do
(
sourceEdge = sourceEdgeVerts[sourceEdgeNum]
-- where a match for its verts has been found...
-- (sneaky bitArray matching!)
if ((sourceEdge * vertsMatched).numberSet == 2) do
(
-- Translate this edge over to the target-vert numbers:
searchEdge = (for vertNum in sourceEdge collect matchArray[vertNum].targetVert) as bitArray
-- find the matching edge on the target mesh:
keepLooking = true
for targetEdgeNum = 1 to targetEdgeVerts.count where not matchedTargetEdges[targetEdgeNum] while keepLooking do
(
if ((searchEdge - targetEdgeVerts[targetEdgeNum]).numberSet == 0) do
(
-- Convert edge-data to arrays, now we're done with checking them as bitArrays
edgeMatches[sourceEdgeNum] = targetEdgeNum
sourceEdgeVerts[sourceEdgeNum] = sourceEdgeVerts[sourceEdgeNum] as array
targetEdgeVerts[targetEdgeNum] = targetEdgeVerts[targetEdgeNum] as array
matchedSourceEdges[sourceEdgeNum] = true
matchedTargetEdges[targetEdgeNum] = true
newMatchedEdges[sourceEdgeNum] = true
keepLooking = false
)
)
)
)
-- USE THE NEWLY-MATCHED EDGES TO FIND FACES --
for edgeNum in newMatchedEdges do
(
-- Get unmatched faces using this source-mesh edge:
sourceEdgeFaces = (polyop.getFacesUsingEdge baseSourceObj edgeNum) - matchedSourceFaces
if (sourceEdgeFaces.numberSet != 0) do
(
sourceEdgeFaces = sourceEdgeFaces as array
-- Get unmatched faces using this target-mesh edge:
targetEdgeNum = edgeMatches[edgeNum]
targetEdgeFaces = ((polyop.getFacesUsingEdge baseTargetObj targetEdgeNum) - matchedTargetFaces) as array
sourceVerts = sourceEdgeVerts[edgeNum]
targetVerts = targetEdgeVerts[targetEdgeNum]
sourceFaceVertLists = for faceNum in sourceEdgeFaces collect (polyop.getFaceVerts baseSourceObj faceNum)
targetFaceVertLists = for faceNum in targetEdgeFaces collect (polyop.getFaceVerts baseTargetObj faceNum)
-- Order faceLists to put our edge-verts at start of list:
-- This allows us to compare vert-order on faces, then find further vert-matches.
for n = 1 to sourceEdgeFaces.count do
(
sourceFaceVertLists[n] = edgeOrderFace sourceFaceVertLists[n] sourceVerts[1] sourceVerts[2]
)
for n = 1 to targetEdgeFaces.count do
(
targetFaceVertLists[n] = edgeOrderFace targetFaceVertLists[n] targetVerts[1] targetVerts[2]
)
newMatchedFaces = #{}
for sourceFaceNum = 1 to sourceFaceVertLists.count do
(
-- Find the adjacent face to this edge where the matched verts go in the same direction,
-- and use those matched faces to match some more verts
sourceVerts = sourceFaceVertLists[sourceFaceNum]
checkVert = matchArray[sourceVerts[1]].targetVert
keepLooking = true
for targetFaceNum = 1 to targetFaceVertLists.count where not newMatchedFaces[targetFaceNum] while keepLooking do
(
targetVerts = targetFaceVertLists[targetFaceNum]
-- This makes sure the correct face is used of the two possible on this edge
if (targetVerts[1] == checkVert) do
(
-- Don't do vert-matching for faces with different vert-counts
if (sourceVerts.count == targetVerts.count) do
(
-- We have a winner!
matchedSourceFaces[sourceFaceNum] = true
matchedTargetFaces[targetFaceNum] = true
newMatchedFaces[targetFaceNum] = true
keepLooking = false
-- Now we can match up the unmatched verts on this face:
for vertNum = 1 to sourceVerts.count do
(
sourceVert = sourceVerts[vertNum]
if not vertsMatched[sourceVert] do
(
vertsMatched[sourceVert] = true
matchArray[sourceVert] = vertMatchData sourceVert:sourceVert targetVert:targetVerts[vertNum]
if debugVertMove do
(
polyop.setVert baseTargetObj targetVerts[vertNum] (polyop.getVert baseSourceObj sourceVert) node:baseSourceObj
--polyop.setVertColor baseTargetObj 0 targetVerts[vertNum] green
)
)
)
)
)
)
)
)
)
if debugVertMove do
(
redrawViews()
)
)
if not debugVertMove do
(
progressEnd()
)
if debugVertMove do
(
for item in matchArray where (item != undefined) do (unmatchedVerts[item.targetVert] = false)
--polyop.deleteVerts baseTargetObj unmatchedVerts
)
if notCancelled then
(
local outArray = #(#(),#())
for n = vertsMatched do
(
append outArray[1] matchArray[n].sourceVert
append outArray[2] matchArray[n].targetVert
)
outArray
)
else false
),
fn getStartVert obj =
(
local baseObj = if (isProperty obj #baseObject) then obj.baseObject else obj
local vertSel = (polyop.getVertsByFlag baseObj 1) as array
local objName = if (isProperty obj #name) then obj.name else ""
case of
(
(vertSel.count == 0):
(
messageBox ("No vertices selected on object " + objName) title:"No verts selected on object"
false
)
(vertSel.count > 1):
(
messageBox ((vertSel.count as string) + " verts selected on object " + objName + "\nPlease select only one!") title:"Too many verts selected on object"
false
)
default:
(
vertSel[1]
)
)
),
fn getVertTransferData obj =
(
local selObjs = selection as array
local baseObj = if (isProperty obj #baseObject) then obj.baseObject else obj
local skinMod = obj.modifiers[#skin]
if (getCommandPanelTaskMode() != #modify) do (setCommandPanelTaskMode #modify)
if (modPanel.getCurrentObject() != skinMod) do (modPanel.setCurrentObject skinMod)
local vertData
if ((skinOps.GetNumberBones skinMod) == 0) then
(
messageBox ((obj as string) + "\nObject's Skin modifier has no bones!")
)
else
(
local boneCount, boneIDs, boneWeights
vertData = for vertNum = 1 to (polyop.getNumVerts baseObj) collect
(
boneCount = skinOps.getVertexWeightCount skinMod vertNum
boneIDs = for boneNum = 1 to boneCount collect
(
skinOps.getVertexWeightBoneId skinMod vertNum boneNum
)
boneWeights = for boneNum = 1 to boneCount collect
(
skinOps.getVertexWeight skinMod vertNum boneNum
)
#(boneIDs, boneWeights)
)
)
select selObjs
if (vertData == undefined) then (return undefined) else
(
struct RSskinXferData (sourcePoly, vertData, transform, skinMod)
return RSskinXferData (copy obj.baseObject) vertData obj.transform (copy skinMod)
)
),
fn pasteVertTransferData obj copyData sourceStartVert targetStartVert =
(
local sourcePoly = copyData.sourcePoly
local sourcePatternHash = getUniquePatternHashLevel sourcePoly sourceStartVert
local vertData = copyData.vertData
obj.transform = copyData.transform
local skinMod = obj.modifiers[#skin]
if skinMod == undefined do
(
skinMod = copy copyData.skinMod
addModifier obj skinMod
)
local baseObj = obj.baseObject
local vertTreeNum = findPatternTreeMatch baseObj targetStartVert sourcePatternHash
-- Pattern-match failure:
if (vertTreeNum == undefined) do
(
return false
)
local matchVerts = matchObjVerts sourcePoly baseObj sourceStartVert targetStartVert targetStartEdge:vertTreeNum
local sourceVertNums = matchVerts[1]
local targetVertNums = matchVerts[2]
if (getCommandPanelTaskMode() != #modify) do (setCommandPanelTaskMode #modify)
if (modPanel.getCurrentObject() != skinMod) do (modPanel.setCurrentObject skinMod)
--skinOps.resetAllBones skinMod
local sourceVert, targetVert
for n = 1 to sourceVertNums.count do
(
sourceVert = sourceVertNums[n]
targetVert = targetVertNums[n]
local DEBUGDATA = #(#ReplaceVertexWeights, skinMod, n, sourceVert, targetVert, vertData[sourceVert])
skinOps.ReplaceVertexWeights skinMod targetVert vertData[sourceVert][1] vertData[sourceVert][2]
)
redrawViews()
return true
),
fn copyVerts =
(
local StartTime = TimeStamp()
local objSel = for obj in selection where (isKindOf obj.baseObject Editable_Poly) collect obj
case of
(
(objSel.count < 2):
(
messageBox ("Please select two Editable Poly objects") title:"Not enough suitable objects selected"
return false
)
(objSel.count > 2):
(
messageBox ("Please select only two Editable Poly objects") title:"Too many objects selected"
return false
)
)
local objA = $[1]
local objB = $[2]
-- Ensure that both meshes are nice and clean before examining them:
polyOp.collapseDeadStructs objA
polyOp.collapseDeadStructs objB
local objAstartVert = getStartVert objA
if objAstartVert == false do return false
local objBstartVert = getStartVert objB
if objBstartVert == false do return false
local patternHash = getUniquePatternHashLevel objA objAstartVert
local startEdgeNum = findPatternTreeMatch objB objBstartVert patternHash
if (startEdgeNum == undefined) do
(
messageBox ("Topology-match not found!") title:"Edges around selected verts are too different"
return false
)
local matchVerts = matchObjVerts objA objB objAstartVert objBstartVert targetStartEdge:startEdgeNum debugVertMove:true
local vertNumsA = matchVerts[1]
local vertNumsB = matchVerts[2]
format "Matched: %\n" vertNumsA.count
for n = 1 to matchVerts[1].count do
(
-- polyop.setVert objB vertNumsB[n] (polyop.getVert objA vertNumsA[n])
)
redrawViews()
select objB
format "\nTook % seconds\n\n" ((TimeStamp() - StartTime) / 1000.0)
print matchVerts
matchVerts
),
-- Called by on viewport-redraw by RStransferSkinDataDrawCallback:
fn viewportDraw =
(
gw.setTransform (Matrix3 1)
local objColours = #(green, red)
local objDataArray = ::RsSkinDataTransferRoll.objVertSelData
for dataNum = 1 to objDataArray.count do
(
local objData = objDataArray[dataNum]
local obj = objData.obj
if (isValidNode obj) and (not obj.isHidden) do
(
local objBase = if isProperty obj #baseObject then obj.baseObject else obj
local objColour = objColours[dataNum]
local vertNums = objData.elemStartVerts
local chooseVert = objData.chooseVert
local vertNum, drawPos
for n = 1 to vertNums.count where (vertNums[n] != undefined) do
(
vertNum = vertNums[n]
drawPos = gw.wtranspoint (polyop.getvert objBase vertNum node:obj) + [1,1,0]
--gw.wMarker drawPos #asterisk color:white--objColour
--gw.wMarker (drawPos + [-1,0,0]) #smallHollowBox color:white--objColour
gw.wRect (box2 [drawPos.x - 3, drawPos.y - 3] [drawPos.x + 4, drawPos.y + 3]) objColour
gw.wMarker drawPos #circle color:white
gw.wtext drawPos (n as string) color:white
)
if (chooseVert != undefined) and ((findItem vertNums chooseVert) == 0) do
(
drawPos = gw.wtranspoint (polyop.getvert objBase chooseVert node:obj) + [1,1,0]
gw.wMarker drawPos #asterisk color:yellow
--gw.wMarker drawPos #circle color:white
--gw.wtext drawPos (chooseVert as string) color:yellow
)
)
)
),
fn hideTheseElements obj verts =
(
if isValidNode obj do
(
local faceSelectWas = polyop.getFaceSelection obj
obj.unhideAll #Face
for vertNum in verts where (isKindOf vertNum integer) do
(
local vertFaces = polyop.getFacesUsingVert obj vertNum
local elementFaces = polyop.getElementsUsingFace obj vertFaces
polyop.setFaceSelection obj elementFaces
obj.hide #Face
)
polyop.setFaceSelection obj faceSelectWas
)
)
)
global gRsSkinDataTransfer = RsSkinDataTransfer()