fn slicePoly obj pos vector =
(
local verts = obj.verts as bitArray
obj.selectededges = #{}
if (slice obj #all (ray pos vector)) do
(
deleteVerts obj (for vert in verts where isPositiveDir (getvertpos obj vert) vector pos collect vert)
cap obj #selection
)
)
Not really, at least with small amount of clusters, but using the returned value from the Slice() method saves around 1% in 252 clusters compared with counting the vertices, so with larger amounts it migh save a little more.
try (destroyDialog ::NightmareFracture) catch()
rollout NightmareFracture "NightmareFracture" width:200
(
local obj, points
spinner seed_sp "Seed: " type:#integer range:[0,1e9,1] fieldwidth:56 align:#right offset:[8,0]
spinner number_sp "Number Cuts: " type:#integer range:[1,10000,100] fieldwidth:56 align:#right offset:[8,0]
button create_bt "Create Test Scene" width:192 align:#right offset:[8,0]
checkbox disable_ref_ch "Disable Ref Messages" offset:[0,4]
button fracture_bt "Fracture" width:192 align:#right offset:[8,0]
label info_lb "time: 0" align:#left
on create_bt pressed do
(
delete objects
if seed_sp.value != 0 do seed seed_sp.value
obj = Box lengthSegs:1 widthSegs:1 heightSegs:1 length:200 width:200 height:200 wirecolor:orange
_min = obj.min*0.9
_max = obj.max*0.9
points = for k=1 to number_sp.value collect (random _min _max)
gc()
)
local getvertpos = polyOp.getVert
local deleteVerts = polyOp.deleteVerts
local deletefaces = polyOp.deletefaces
local cap = polyOp.capHolesByEdge
local capHolesByVert = polyop.capHolesByVert
local createPolygon = polyop.createPolygon
local slice = polyOp.slice
local retriangulate = polyop.retriangulate
local getvertsusingedge = polyop.getvertsusingedge
local getfacecenter = polyop.getfacecenter
local getfacesusingvert = polyop.getfacesusingvert
local getfacesusingedge = polyop.getfacesusingedge
local getelementsusingedge = polyop.getelementsusingface
fn isPositiveDir pos vector vectorPos = dot vector (pos - vectorPos) > 0
fn slicePoly obj pos vector =
(
local verts = #{1..obj.numverts}
obj.selectedverts = #{}
if (slice obj #all (ray pos vector)) do
(
_verts = #{}
for vert in verts where isPositiveDir (getvertpos obj vert) vector pos do append _verts vert
deleteVerts obj _verts
capHolesByVert obj #selection
)
)
on fracture_bt pressed do if obj != undefined AND points != undefined do undo off
(
local t1 = timeStamp()
local pointCount = points.count
setCommandPanelTaskMode mode:#create
if disable_ref_ch.state do disablerefmsgs()
with redraw off
(
local tempMesh = copy obj ishidden:on
converttopoly tempMesh
local chunks = for p in points collect copy tempMesh prefix:#voro pivot:p --wirecolor:orange
t2 = timeStamp()
for p1 = 1 to pointCount - 1 do for p2 = p1 + 1 to pointCount while not keyboard.escpressed do
(
local pos = points[p1]
local vec = (points[p2] - pos) / 2
slicePoly chunks[p1] (pos + vec) vec
slicePoly chunks[p2] (pos + vec) -vec
)
delete tempMesh
if disable_ref_ch.state do enablerefmsgs()
t3 = timeStamp()
info_lb.text = "time: " + (t3 - t1) as string
format "chuncks:% time: % %
" pointCount (t2 - t1) (t3 - t1)
)
unhide chunks
)
)
createDialog NightmareFracture
here is a little redesigned UI for better testing
In the slicePoly() function, removing:
obj.selectedverts = #{}
and using:
capHolesByVert obj #all
gives me a 20-30% speed increase for 200 cuts seed 1.
fn slicePoly obj pos vector =
(
local verts = #{1..obj.numverts}
--obj.selectedverts = #{}
if (slice obj #all (ray pos vector)) do
(
_verts = #{}
for vert in verts where isPositiveDir (getvertpos obj vert) vector pos do append _verts vert
deleteVerts obj _verts
capHolesByVert obj #all
)
)
capHolesByVert obj #all
this is not correct actually. it has to be #selection. you can see what’s wrong trying to chop a teapot for example.
that’s also why concave geometry cannot be chopped
Really? It might be wrong in theory but in practice I see no difference. Would need more testing though.
in the code above there are two steps of making chunks:
create node duplicates
chop duplicates
i’ve completely rewritten all the code on c++ sdk
and the result is not very impressive
create node duplicates is only 20% faster
chop duplicates is ~1.5-2.5 times faster (depending on number of cuts and source’s mesh polycount)
but it looks like the same fast(or slow) as the rayfire modifier. and for sure faster than IShatter. the mxs code above is faster than IShatter
so…
stop using maxscript: a dead language
don’t bury anything before you don’t really know what it is
the cool feature of the rayfire modifier i see is preserving normals (visually seamless cuts).
it’s not easy. it seems like to preserve normals we have to slice two times. first time without separation and the next one with. … by storing in between two slices new vertex normals, and applying them after final slice
Yes, that’s cool.
Another good feature is to work with concave geometry. All methods showed here are only valid for convex geometry, which is very limited.
I don’t think you need to slice twice because by default the split property is false so you’re currently slicing without actually splitting into separate elements and the vertex delete is handling the unwanted verts (elements) ok.
Perhaps you could store the normal in a map channel for each new vert?
I know it’s not “politically correct”, but if speed is the most important, avoiding functions and replacing them by the code, you save between 5% and 10% of time.
That’s what I mean (erasing ‘fn isPositiveDir’ and ‘fn slicePoly’):
try (destroyDialog ::NightmareFracture) catch()
rollout NightmareFracture "NightmareFracture WITHOUT_FN" width:200
(
local obj, points
spinner seed_sp "Seed: " type:#integer range:[0,1e9,1] fieldwidth:56 align:#right offset:[8,0]
spinner number_sp "Number Cuts: " type:#integer range:[1,10000,100] fieldwidth:56 align:#right offset:[8,0]
button create_bt "Create Test Scene" width:192 align:#right offset:[8,0]
checkbox disable_ref_ch "Disable Ref Messages" offset:[0,4]
button fracture_bt "Fracture" width:192 align:#right offset:[8,0]
label info_lb "time: 0" align:#left
on create_bt pressed do
(
delete objects
if seed_sp.value != 0 do seed seed_sp.value
obj = Box lengthSegs:1 widthSegs:1 heightSegs:1 length:200 width:200 height:200 wirecolor:orange
_min = obj.min*0.9
_max = obj.max*0.9
points = for k=1 to number_sp.value collect (random _min _max)
gc()
)
local getvertpos = polyOp.getVert
local deleteVerts = polyOp.deleteVerts
local deletefaces = polyOp.deletefaces
local cap = polyOp.capHolesByEdge
local capHolesByVert = polyop.capHolesByVert
local createPolygon = polyop.createPolygon
local slice = polyOp.slice
local retriangulate = polyop.retriangulate
local getvertsusingedge = polyop.getvertsusingedge
local getfacecenter = polyop.getfacecenter
local getfacesusingvert = polyop.getfacesusingvert
local getfacesusingedge = polyop.getfacesusingedge
local getelementsusingedge = polyop.getelementsusingface
on fracture_bt pressed do if obj != undefined AND points != undefined do undo off
(
local t1 = timeStamp()
local pointCount = points.count
setCommandPanelTaskMode mode:#create
if disable_ref_ch.state do disablerefmsgs()
with redraw off
(
local tempMesh = copy obj ishidden:on
converttopoly tempMesh
local chunks = for p in points collect copy tempMesh prefix:#voro pivot:p --wirecolor:orange
t2 = timeStamp()
for p1 = 1 to pointCount - 1 do for p2 = p1 + 1 to pointCount while not keyboard.escpressed do
(
pos = points[p1]
vec = (points[p2] - pos) / 2
obj1 = chunks[p1]
pos1 = pos + vec
verts = #{1..obj1.numverts}
obj1.selectedverts = #{}
if (slice obj1 #all (ray pos1 vec)) do
(
_verts = #{}
for vert in verts where (dot vec ((getvertpos obj1 vert) - pos1) > 0) do append _verts vert
deleteVerts obj1 _verts
capHolesByVert obj1 #selection
)
obj2 = chunks[p2]
vec2 = -vec
verts = #{1..obj2.numverts}
obj2.selectedverts = #{}
if (slice obj2 #all (ray pos1 vec2)) do
(
_verts = #{}
for vert in verts where (dot vec2 ((getvertpos obj2 vert) - pos1) > 0) do append _verts vert
deleteVerts obj2 _verts
capHolesByVert obj2 #selection
)
)
delete tempMesh
if disable_ref_ch.state do enablerefmsgs()
t3 = timeStamp()
info_lb.text = "time: " + (t3 - t1) as string
format "chuncks:% time: % %
" pointCount (t2 - t1) (t3 - t1)
)
unhide chunks
)
)
createDialog NightmareFracture
Not sure where I got this code. But this is what I use to speed things up by shutting redraw on the command panels
fn FN_Stop_Command_Redraw = (
WM_SETREDRAW=0xB
commandHWND = (windows.getChildHWND #max "Command Panel")
if commandHWND != undefined and classOf commandHWND==Array then commandHWND=commandHWND[1]
else commandHWND = windows.getmaxhwnd()
if commandHWND !=undefined do windows.sendmessage commandHWND WM_SETREDRAW 0 0
)
fn FN_Resume_Command_Redraw = (
WM_SETREDRAW=0xB
commandHWND = (windows.getChildHWND #max "Command Panel")
if commandHWND != undefined and classOf commandHWND==Array then commandHWND=commandHWND[1]
else commandHWND = windows.getmaxhwnd()
if commandHWND !=undefined do windows.sendmessage commandHWND WM_SETREDRAW 1 0
)
It is not a drawing issue what slows the code, but the Slicing function. Not even disabling the messages will save time.
As I mentioned before, just making sure that Slice() succeeded saves 80% over the already optimized code, which is not a trivial improvement.
However, there are still too many unnecessary calls and checks.
Using selectedverts would be slow then just passing a bit array of verts. (I think)
obj1.selectedverts
why setCommandPanelTaskMode mode:#create