Ok I was right.
before time: 335
After comment out…
line 45
–setCommandPanelTaskMode mode:#create
line 64
–obj1.selectedverts = #{}
line 76
– obj1.selectedverts = #{}
after time: 299
After this you need to find the verts and not use #selection
capHolesByVert obj2 #selection
#selection this will make it slow too
also i found that algorithm works wrong if we remove verts above plane only if cut was successful. we have to remove them anyway, but cap only if cut was happened.
So you mean the correct function for Slicing would be something like:
fn slicePoly obj pos vector =
(
local verts = obj.verts as bitArray
obj.selectedverts = #{}
sliced = slice obj #all (ray pos vector)
deleteVerts obj (for vert in verts where isPositiveDir (getVertPos obj vert) vector pos collect vert)
if sliced do polyop.capHolesByVert obj #selection
)
here is what i finally have for the test ‘box’ and 100 chunks (cuts):
pure mxs:
chuncks:100 time: 12 454 2 = 468
mxs + (c++ sdk) extension where i combined slice, remove over plane verts, and cap in one function:
chuncks:100 time: 12 155 2 = 169
full (c++ sdk):
chuncks:100 time: 10 111 1 = 123
time is:
first number is the time to make template chunks
second number is the fracturing time
third number is replacing baseobjects for template chunks
all operations a little faster for editable poly baseobject vs editable poly node.
yes… this is correct. try to cut a teapot. and you will see that spout in case of #all will be capped
Well, you will be disappointed if you try with a tube. So the challenge to make it work with concave geometries remains open.
these are numbers for ‘teapot’ with 8 segments used:
mxs
chuncks:100 time: 100 1775 2 = 1877
extended mxs
chuncks:100 time: 55 492 2 = 549
#full sdk
chuncks:100 time: 42 471 2 = 515
bool PolyCut(MNMesh* pmesh, EPoly* epi, Point3 normal, Point3 pos, float threshold = 0.0f)
{
BitArray verts(pmesh->numv);
pmesh->VertexSelect(verts);
BitArray edges(pmesh->nume);
pmesh->EdgeSelect(edges);
float offset = DotProd(pos, normal);
bool result = pmesh->Slice(normal, offset, threshold, false, false);
if (epi)
{
BitArray verts(pmesh->numv);
for (int k=0; k < pmesh->numv; k++)
{
if (DotProd(pmesh->P(k) - pos, normal) < 0) verts.Set(k);
}
BitArray selverts;
pmesh->getVertexSel(selverts);
verts &= ~selverts;
epi->EpfnPropagateComponentFlags(MNM_SL_VERTEX, MN_TARG, MNM_SL_VERTEX, MN_TARG, FALSE, FALSE, FALSE);
epi->EpSetVertexFlags(verts, MN_TARG, false);
epi->EpfnDelete(MNM_SL_VERTEX, MN_TARG, true);
}
if (result)
{
MNMeshBorder border;
pmesh->GetBorder(border, MNM_SL_EDGE);
pmesh->FillInBorders(&border);
}
return result;
}
this is c++ version (for who is interested) which gave me the best performance
I suppose the only way to make it faster if as I suggested in the first post, building the geometry from scratch.
VoroFrag is pretty fast, keeps the normals, and has a bunch of other features.
How to cap what?
(
delete objects
vf = VoroFrag()
vf.numChunks = 200
addmodifier (tube()) vf
)
Then just Detach All or you could also rebuild the elements manually.
With your permission guys (Juzwa, Swordslayer, denisT) I’ve updated the tool to create other geometries for testing.
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]
radiobuttons geo_rb "Geometry:" labels:#("Box", "Tube", "C", "Sphere", "Torus", "2D") columns:3 align:#left
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 = case geo_rb.state of
(
1: Box lengthSegs:1 widthSegs:1 heightSegs:1 length:200 width:200 height:200
2: Tube heightsegs:1 capsegs:1 sides:24 radius1:40 radius2:60 height:200
3: C_Ext Back_Length:-100 Side_Length:-150 Front_Length:-200 Back_Width:20 Side_Width:20 Front_Width:20 height:200
4: Sphere radius:100 segs:24
5: Torus smooth:2 segs:24 sides:12 radius1:100 radius2:20
6: Box lengthSegs:1 widthSegs:1 heightSegs:1 length:200 width:200 height:10
)
obj.wirecolor = orange
_min = obj.min*0.9
_max = obj.max*0.9
converttopoly obj
obj.weldThreshold = 0.1
polyop.weldVertsByThreshold obj #all
if geo_rb.state == 6 do (_min.z=0;_max.z=0)
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 = #{}
sliced = slice obj #all (ray pos vector)
if verts.numberset > 1 do deleteVerts obj (for vert in verts where isPositiveDir (getVertPos obj vert) vector pos collect vert)
if sliced do 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)
)
select chunks
)
)
createDialog NightmareFracture
try to cap teapot with cap modifier, convert it to mesh or poly, and try to apply VoroFrag
I believe it is from the same developer as RayFire, so as it is free it might has bugs.
I see what you mean, perhaps it does not play well with intersecting geometry?
I’ve moved the Teapot’s handle, spout and lid appart and it seems to work fine.
all that we did before with editable poly was wrong.
everything is much faster, correct with normals, and solves ‘concave’ cap situation if we do it in editable mesh (or trimesh)
M is mesh we need to cut and cap
p is slicing plane
we need:
create mesh plane using plane p big enough to cut M
make subtraction M-P
it will slice M but not separate, select new verts, and make cap(selected faces!)
delete verts below plane
we can call meshop slice method with option ‘remove’, but it seem like it will be double work
weld selected verts and verts from cap
(optional) we can delete plane P or keep it for next cut.
that’s it
You could use a box instead of a plane and avoid all the capping and vertex deleting.
Even though, I haven’t had a good experience with mesh operators. I don’t think this will be any faster but the contrary.
we have to compare meshop and polyop slice methods with things being equal… so it has to be the same TRI count to slice
Where did that rule come from?
We are trying to improve this method using whatever can be used within MXS.
If you want to know if polyop.slice is faster than meshop.slice, that’s easy know.
If you want to know if mesh subtraction is faster than slicing that’s also easy to know.
The only possible optimization I see using this method is to test if the plane intersects the cluster before doing the slicing. But that may be more expensive than the slicing itself.
Using the build-in cap function doesn’t seem to work with all concave geometries, but at least works with convex objects.
Mesh subtraction will be slower I guess, so it remains open the question if meshop.slice will be any faster. But then you need to cap the cluster manually, and that will probably kill all performance gained by using meshop, moreover for concave geometries.
it’s not a rule. it’s a simple logic.
the way to find an intersection of a segment (edge in our case) and a plane was known B.C.
there is no any faster solution… if only some key data was smartly cached.
that’s why it makes sense to compare mesh and poly slice methods.
(
delete objects
pos = [0,0,50]
dir = [0.5,-0.5,0.5]
obj1 = converttomesh (box lengthsegs:1 widthsegs:1 heightsegs:1 length:100 width:100 height:100 wirecolor:red)
obj2 = copy obj1 pos:[150,0,0] wirecolor:green
slicer1 = converttomesh (plane length:200 width:200 lengthsegs:1 widthsegs:1 pos:pos dir:-dir)
slicer2 = converttomesh (box lengthsegs:1 widthsegs:1 heightsegs:1 length:200 width:200 height:200 pos:(pos+[150,0,0]) dir:dir)
-- Whats the benefit of doing this...? -----------------------------------------------------
verts = #{1..obj1.numverts}
obj1 - slicer1
meshop.deleteverts obj1 (for v in verts where dot dir (getvert obj1 v - pos) > 0 collect v)
meshop.weldVertsByThreshold obj1 #selection 0.1
-- Instead of this...? ---------------------------------------------------------------------
obj2 - slicer2
--------------------------------------------------------------------------------------------
delete #(slicer1, slicer2)
)