--Created by Norman Schaar: norman3d@gmail.com
macroScript FiberMeshToSpline
category:"Norman3D"
buttontext:"FiberMesh to Spline"
tooltip:"Convert Fiber Mesh to Spline"
(
if selection.count != 1 then
(
messagebox "You have to select a mesh"
)
else
(
theMesh = selection[1]
if classof theMesh == Editable_Poly then
(
start = timeStamp()
with undo off with redraw off
(
progressStart "Creating splines..."
theMesh = selection[1]
--Get total vert count of mesh
theVertCount = polyop.getNumVerts theMesh
--Get total face count of mesh
theFacesCount = theMesh.getNumFaces()
--A bitArray to hold all the face IDs that were either checked or were part of elements of other checked faces.
usedFaces = #{}
--These variables we only need to get once. Each hair strand has the same amount of verts and sides.
global theSidesCount = undefined
global theVerts = undefined
--All the faces
theFaces = #{1..theFacesCount}
--We Create a splineshape
sp = splineShape()
--We will be keeping track of the element number
local ElementNumber = 0
--This is for the progressbar
global progressVertCount = 0
--For each face in "all the faces" that has not been checked yet:
for f in theFaces where not usedFaces[f] do
(
ElementNumber = ElementNumber + 1
local elem = polyop.getElementsUsingFace theMesh #{f}
usedFaces += elem --We add the faces of the current element to the bitarray so we won't check the remaining faces.
--We get the edges using the faces. We will need the first edge of the elemet.
local TheEdges = polyop.getEdgesUsingFace theMesh elem
--We get the Vertcount of an element.
if theVerts == undefined do
(
local theVerts = polyop.getVertsUsingEdge theMesh theEdges
global VertCount = theVerts.numberSet
)
--We update the progressbar
global progressVertCount = progressVertCount + VertCount
progressUpdate (progressVertCount / (theVertCount as float) * 100.0)
--We get the first edge in "theEdges" bitarray. This seems to be the fastest way.
local theEdge = (theEdges as array)[1]
--Here are some other methods I tried:
/*
--Method 1: (Causes garbage collection error)
local theEdge = for n in TheEdges do exit with n
--Method 2: (It's fast but ugly)
local theString = theEdges as string
local TheEdge = (substring theString 3 ((findString theString "..") - 3)) as integer
local TheEdge = (filterString (TheEdges as string) ".")[1] as integer
--Method 3: (Proposed by DenisT, slower than "(theEdges as array)[1]"
fn firstBit arr =
(
local b
for n in arr while (b = n; off) do ()
b
)
local TheEdge = firstBit theEdges
*/
--Get hair sides count
if theSidesCount == undefined do
(
--We select the first edge
theMesh.EditablePoly.SetSelection #Edge #{TheEdge}
--We click loop (Is there a better way of doing this?)
theMesh.EditablePoly.buttonOp #SelectEdgeLoop
global theSidesCount = theMesh.EditablePoly.ConvertSelection #Edge #Vertex
)
--The Hair segments: Since we have the total vertcount and the amount of vertices per segment, we can figure out how many segments there are.
local theSegments = VertCount / theSidesCount
--We add a spline for the element.
local theSplineIndex = addnewspline sp
--For each segment in the hair element, create a knot
for i=1 to (theSegments) do
(
--The vert number of every vert of a segment
local FirstVert = (((ElementNumber - 1) * VertCount) + 1) + ((i - 1) * theSidesCount)
local LastVert = ((ElementNumber - 1) * VertCount) + ((i - 1) * theSidesCount) + theSidesCount
--Get an array of the vert numbers of the particular segment
local theVertexIdArray = for j=FirstVert to LastVert collect j
--Now we need to calculate the knot's position in space. We sum the segment's vertices position and calculate the average. This way the knot is in the center of the vertices.
local sumVal = [0,0,0]
for val in theVertexIdArray do (sumVal += (polyop.GetVert theMesh val))
local sumVal = sumVal/theSidesCount
--Adding the knot
addknot sp theSplineIndex #corner #line sumVal
)
)
--After all the splines have been aded we update the shape, change the pivot point, and that's it!
updateshape sp
sp.pivot = theMesh.pivot
progressEnd()
end = timeStamp()
format "Processing took % seconds\n" ((end - start) / 1000.0)
)
)
else
(
messagebox "The selected mesh must be an Editable Poly"
)
)
)