And here is the compact version which has a little penalty in speed but looks clearer, as Denis suggested.
(
fn BreakAndWeldAllUvwVerts obj =
(
mesh = snapshotasmesh obj
setnumtverts obj (obj.numfaces*3)
buildtvfaces obj
verts = for j = 1 to obj.numverts collect #()
idx = deepcopy verts
for j = 1 to obj.numfaces do
(
f1 = getface obj j
f2 = gettvface mesh j
f3 = [j*3,j*3-1,j*3-2]
for i = 1 to 3 do
(
p = gettvert mesh f2[i]
if (k = finditem verts[f1[i]] p) == 0 then
(
append verts[f1[i]] p
append idx[f1[i]] f3[i]
)
else f3[i] = idx[f1[i]][k]
settvert obj f3[i] p
)
settvface obj j f3
)
)
gc()
st = timestamp(); sh = heapfree
BreakAndWeldAllUvwVerts $
format "faces:% time:% ram:%
" $.numfaces (timestamp()-st) (sh-heapfree)
)
Hey Jorge
You said that second for-loop causes slowdown
I don’t know if mapped fn can helps here but still…
I mean about this concept. Maybe it can replace “for i = 1 to 3 do ()”
verts = #()
idx = #()
a = #(1,2,3)
mapped fn test a &verts &idx =
(
verts[a] = "v" + a as string
idx[a] = "i" + a as string
)
test a &verts &idx
Thank you Branko for bringing this up. Ive tried mapping the inner loop as well as the main loop and both options were slower and used more memory.
The compact version, is not a lot slower as is, but I was losing half of the optimization I did. So I went from 1000 down to 500 and then up to 750 (aprox.)
Anyway, for 100K mesh 250ms is nothing if you like the clearer code better.
Great result you achive here. Then mapped fn have same performance issue as for-loop.
Good to know that. Also adding items like I did maybe is not good way. Append fn probably produce faster result.
Almost no difference (6 ms in a 100K mesh). On a fresh session I got:
#55 faces:99380 time:560 ram:16968168L
#56 faces:99380 time:715 ram:16968200L
#60 faces:99380 time:709 ram:16968168L
I am not sure the 560ms version could be much faster, as profiling it show that around 300 ms goes just to the get/set vertex and face operations, and the other 260 are for the if statements, append and finditem.
here is another optimization… the speed is almost the same as the best but memory is used better, and code in better organized:
fn breakAndWeldUVWs node: =
(
converttomesh node
tverts = for v=1 to node.numtverts collect gettvert node v
mesh = snapshotasmesh node
meshop.applyuvwmap node #face
verts = for v=1 to node.numverts collect #()
index = deepcopy verts
for f=1 to node.numfaces do
(
f1 = getface node f
f2 = gettvface node f
f3 = gettvface mesh f
for i=1 to 3 do
(
if (k = finditem verts[f1[i]] tverts[f3[i]]) == 0 then
(
append verts[f1[i]] tverts[f3[i]]
append index[f1[i]] f2[i]
settvert node f2[i] tverts[f3[i]]
)
else f2[i] = index[f1[i]][k]
)
settvface node f f2
)
--meshop.deleteisomapvertsall node -- it's what we have to do finally
node.numfaces
)
gc light:on
t1 = timestamp()
m1 = heapfree
num = breakAndWeldUVWs node:objects[1]
format "faces:% time:% memory:%
" num (timestamp() - t1) (m1 - heapfree)
i do convert to mesh to make the function undoable… and we also have to delete isolate map verts
Yes Denis, that used less memory and I forgot to delete the isolated vertices.
our appends take 80% of memory and performance. so probably the current algorithm reached the bounds
Ok, here is the final one with converttomesh and delete dead vertices.
(
/*
Version 0.1 | 02.18.14
Original
Version 0.2 | 02.18.14
Speed & Memory optimizations
Version 0.3 | 02.19.14
Improved Algorithm - 2X faster
Version 0.4 | 02.20.14
Add converttomesh and clean dead vertices (Thanks denisT)
/////////////////////////////////////////////////////////////////////////////////////////////////
Description:
This script will break all UVW vertices on the selected mesh and then weld those UVW
vertices that are on the exact same Geometry and UVW position.
/////////////////////////////////////////////////////////////////////////////////////////////////
*/
fn BreakAndWeldAllUvwVerts obj =
(
converttomesh obj
mesh = snapshotasmesh obj
setnumtverts obj (obj.numfaces*3)
buildtvfaces obj
verts = for j = 1 to obj.numverts collect #()
idx = deepcopy verts
for j = 1 to obj.numfaces do
(
f1 = getface obj j
f2 = gettvface mesh j
i3 = j*3
i2 = i3-1
i1 = i3-2
v1 = gettvert mesh f2[1]
v2 = gettvert mesh f2[2]
v3 = gettvert mesh f2[3]
k1 = finditem verts[f1[1]] v1
k2 = finditem verts[f1[2]] v2
k3 = finditem verts[f1[3]] v3
if k1 == 0 then
(
append verts[f1[1]] v1
append idx[f1[1]] i1
)
else i1 = idx[f1[1]][k1]
if k2 == 0 then
(
append verts[f1[2]] v2
append idx[f1[2]] i2
)
else i2 = idx[f1[2]][k2]
if k3 == 0 then
(
append verts[f1[3]] v3
append idx[f1[3]] i3
)
else i3 = idx[f1[3]][k3]
settvert obj i1 v1
settvert obj i2 v2
settvert obj i3 v3
settvface obj j [i1,i2,i3]
)
meshop.deleteisomapvertsall obj
)
gc()
st = timestamp(); sh = heapfree
BreakAndWeldAllUvwVerts $
format "faces:% time:% ram:%
" $.numfaces (timestamp()-st) (sh-heapfree)
)
why do we do convert to mesh? the only reason is to make our “mapping works” undoable. honestly i’ve picked the most sure operation. but who can find most cheaper mesh operation that forces the mesh holds its stage (per se, makes mapping operations undoable)
couple comments:
#1 some mesh operations might be slower if the mesh selected
#2 some mesh operations might be slower if command panel is in #modify mode
it’s because of some notification messages sent by mesh to the system in one ore another situation
Yes, and that was introduced with this
converttomesh obj
Other than that, the algorithm works on the fly and selection doesn’t affect it, neither does the modify panel.
here is my numbers:
--both selected and not selected in #create mode:
faces:49690 time:632 memory:7045248L
--not selected in #modify mode:
faces:49690 time:705 memory:7052448L
--selected in #modify mode:
faces:49690 time:819 memory:7058616L
--selected in #modify mode in subobject mode :
faces:49690 time:881 memory:7062520L
Yes Denis, as I mentioned that was introduced by the mesh convertion. If you comment the following line you would get the same results selected or not, in modify panel or not. I get ±5ms for each test for a 100K mesh.
converttomesh obj
I added it because you suggested it to make the function undoable.
ok. after all please tell me what is the practical use of all we cooked here?