[Closed] Fibermesh to spline: Help me improve it!
does every fiber in the object has the same number of sides and segments?
Haven’t had time to check your script denis, i’m still at work. But I see you are providing the numbers to the functions, so that the script doesn’t have to “find” it. It is of course a lot faster this way! You are probably doing some other things differently, I’ll have a closer look later
I know for a fact that the segment, vert and side number will be the same for each element in the mesh.
However, in Zbrush you can change this number! You can create a fibermesh with 5 segments, instead of 4, or 10 sides instead of 1.
So maxscript should still figure out what those numbers are to work on any possible fibermesh.
That’s why in my script I select edge 1, and click “loop” in order to get the number of sides. Since I also have the amount of verts I can use those numbers to get the amount of segments.
VertNumber / Sides = Segments
I only do this with the first element, since I know all the other elements have the same properties as the first one.
Hey Norman – cool script!
After Wayne posted his (which is more a macrorecord of manual steps) I thought I’d have a go at mine & here’s where I got.
(
--Define Stuff
AllEdges = #{}
AllOpenEdges = #{}
--Do Stuff
if selection.count == 1 then
(
FibreMesh = selection[1]
SplineName = "Spline_"+FibreMesh.name
if classof FibreMesh == Editable_Poly then
(
--Selects the interior edges of each fibre. There absolutely has to be a more elegant way to do this. It is beyond me however!
AllOpenEdges = polyop.GetOpenEdges FibreMesh
InteriorEdges=-AllOpenEdges
polyop.setEdgeSelection FibreMesh InteriorEdges
FibreMesh.SelectEdgeRing()
--Divide the polys to create spline. The much better approach would be to have the splines created from the centre coordinates of each edge but again, this is beyond my skills :)
FibreMesh.ConnectEdges()
EdgesforSpline = (polyop.getEdgeSelection FibreMesh) as array
polyop.CreateShape FibreMesh EdgesforSpline name:SplineName
--Delete the original geometry. Remove this line if you'd still like it.
delete FibreMesh
messagebox "Done!" title:"Tada!"
)
Else messagebox "Please convert to Editable Poly" title:"Sorry!"
)
Else messagebox "Please select (one) object" title:"Oh no!"
)
It won’t work on multi sided strands (I didn’t even realise you could export multi sided hairs!), but it should work on multi segmented strips.
hehe cool! What you describe in the comments is exactly what I’m doing with the script. Taking the coordinates of the vertices of every segments (1, 2 or n). Then sum them and divide them by the number of sides to get the averaged out coordinate. In other words, the center. How fast is the script? I assume it isn’t very fast since you are selecting rings and connecting edges, which might be a problem with pretty high-res meshes.
Edit: Make sure to check my comments in my script. I made sure to explain everything I’m doing step by step.
I’m just going through it now- you’ve done exactly what I wanted to do very glad you’ve detailed your process, it’s a big help!
I’ve tested mine on some fairly complex meshes but I’m guessing the performance will take a hit if things get crazy
i do the same things to get number of sides and segments… but can some fibers of the same mesh have different number of segments? number of sides are probably always the same for all fibers.
Number of sides and and segments are always the same for each fiber in the mesh. I guess I could have gotten rid of some calculations in my first attempt!
By the way, the last version you posted is not properly working on the “strip” version, look:
fn elementToCurve node:selection[1] uniform:off = if iskindof node Editable_Poly do
(
fn getNumberSides node: =
(
node.selectededges = #{1}
node.SelectEdgeLoop()
(polyop.getvertsusingedge node node.selectededges).numberset
)
fn collectElements node: =
(
local getElement = polyop.getElementsUsingFace
local getVerts = polyop.getVertsUsingFace
local elements = #{}, done = #{}, count = 0
for f in (node.faces as bitarray) where not done[f] do
(
ff = getElement node f
vv = getverts node ff
append elements (count += vv.numberset)
join done ff
)
elements
)
fn makeSpline data name: pos:[0,0,0] =
(
sp = splineShape name:name pos:pos wirecolor:orange
for d in data do
(
k = addnewSpline sp
for v in d do addKnot sp k #smooth #line v
)
updateShape sp
sp
)
sides = getNumberSides node:node
if uniform then
(
segments = (polyop.getVertsUsingFace node (polyop.getElementsUsingFace node 1)).numberset/sides
elements = #{}
step = segments*sides
for k=step to node.numverts by step do append elements k
)
else elements = collectElements node:node
local mesh = snapshotasmesh node
local verts = #(), data = #()
local i = 0, pos = [0,0,0]
for v=1 to mesh.numverts do
(
pos += getVert mesh v
if (mod (i += 1) sides) == 0 do
(
append verts (pos/sides)
pos = [0,0,0]
)
if elements[v] do
(
append data verts
verts = #()
)
)
delete mesh
makeSpline data name:(node.name + "_Shape") pos:node.transform.pos
)
/*
(
gc()
node = $Fibers_cylinder
t1 = timeStamp()
elementToCurve node:node uniform:on
format "time: % uniform: % mesh: %
" (timeStamp() - t1) on node.name
gc()
t1 = timeStamp()
elementToCurve node:$Fibers_cylinder
format "time: % uniform: % mesh: %
" (timeStamp() - t1) off node.name
gc()
node = $Fibers_strip
t1 = timeStamp()
elementToCurve node:node uniform:on
format "time: % uniform: % mesh: %
" (timeStamp() - t1) on node.name
gc()
t1 = timeStamp()
elementToCurve node:node
format "time: % uniform: % mesh: %
" (timeStamp() - t1) off node.name
)
*/
time: 77 uniform: true mesh: Fibers_cylinder
time: 249 uniform: false mesh: Fibers_cylinder
time: 311 uniform: true mesh: Fibers_strip
time: 1569 uniform: false mesh: Fibers_strip
uniform means the same number of segments for every fiber.
so for Fibers_cylinder the algorithm takes 0.077 sec, for Fibers_strip it takes 0.311
if number of segments can vary (not uniform) I do the element searching, which takes a time.
the editablePoly interface is very slow. if you can do the same operation using polyop interface do it.
Denis, assuming that fibers are all the same across one mesh, get any element and:
basecount = ((number_of_verts_in_element – number_of_faces_in_element)+1)
baselength = (number_of_verts_in_element / basecount)
So you actually don’t need any loops for this task.
offtopic: Don’t you know why detaching element from mesh completely changes its index arrays (just detach some fibers from sample file and you’ll see wrong result)?