[Closed] Align UV script optimizations
Hi,
I’ve made a script that aligns UV islands to the bottom left. It needs to be faster though.
Can anyone have a look through and see if there are any optimizations possible?
Can I somehow avoid selecting vertices? Are there other methods of selecting UV elements?
Since the script is WIP, there’s some commented out stuff you can ignore. I’ve commented out the actual moving of the UV islands.
Alternative 1
Here I use the selection bounding box to align the islands to the bottom left.
uv = modPanel.getCurrentObject()
TVsub = uv.unwrap2.getTVSubObjectMode()
case TVsub of (
2: (uv.unwrap2.edgeToVertSelect()
uv.unwrap2.setTVSubObjectMode 1)
3: (uv.unwrap2.faceToVertSelect()
uv.unwrap2.setTVSubObjectMode 1)
)
bitarr = uv.unwrap.getSelectedVertices()
bitarrc = bitarr.count
rollout progresspopup "Progress"
(
progressBar progbar value:0.0
)
--createDialog progresspopup
start = timeStamp()
with redraw off
for v = 1 to bitarrc do (
--progresspopup.progbar.value = 100.0*v/bitarrc
if bitarr[v] == 1 then (
uv.unwrap.selectVertices #{v}
--uv.unwrap.selectVertices #{v}
uv.unwrap2.selectElement()
--uv.unwrap2.selectElement()
uv.unwrap2.snapPivot 2
elemoffset=uv.unwrap2.getPivotOffset()
elempos=uv.unwrap2.getSelCenter()
elemmove=elemoffset+elempos
--uv.unwrap2.moveSelected -elemmove
elemarr = #()
elemarr = uv.unwrap.getSelectedVertices() as array
for i = 1 to elemarr.count do
bitarr[elemarr[i]] = 0
)
)
end = timeStamp()
format "Processing took % seconds\n" ((end - start) / 1000.0)
--destroyDialog progresspopup
uv.unwrap2.setTVSubObjectMode TVsub
Alternative 2
Here I put all vertex coordinates in two arrays and find the smallest x and y values.
Slightly slower, but can maybe be made faster.
uv = modPanel.getCurrentObject()
TVsub = uv.unwrap2.getTVSubObjectMode()
case TVsub of (
2: (uv.unwrap2.edgeToVertSelect()
uv.unwrap2.setTVSubObjectMode 1)
3: (uv.unwrap2.faceToVertSelect()
uv.unwrap2.setTVSubObjectMode 1)
)
bitarr = uv.unwrap.getSelectedVertices()
bitarrc = bitarr.count
rollout progresspopup "Progress"
(
progressBar progbar value:0.0
)
--createDialog progresspopup
start = timeStamp()
with redraw off
for v = 1 to bitarrc do (
--progresspopup.progbar.value = 100.0*v/bitarrc
if bitarr[v] == 1 then (
uv.unwrap.selectVertices #{v}
uv.unwrap2.selectElement()
tempselba = uv.unwrap.getSelectedVertices()
tempsel = tempselba as array
temparrx = #()
temparry = #()
for t=1 to tempsel.count do (
--if tempsel[t] == 1 then (
temppos = uv.unwrap.getVertexPosition 0 tempsel[t]
append temparrx temppos[1]
append temparry temppos[2]
--)
)
--xmin = amin temparrx
--ymin = amin temparry
/*
uv.unwrap2.moveSelected [-xmin,-ymin,0]*/
bitarr -= tempselba
)
)
end = timeStamp()
format "Processing took % seconds\n" ((end - start) / 1000.0)
--destroyDialog progresspopup
uv.unwrap2.setTVSubObjectMode TVsub
Grateful for any help
just in case you need a good tool to work with UVs there’s a UVTools
You can start with caching functions
instead of uv.unwrap.getSelectedVertices use
getSelVert = uv.unwrap.getSelectedVertices
...
for i = 1 to ... do
(
vsel = getSelVert()
and also why do you convert bitarrays to arrays?
just substract elem array from bitarr
bitarr -= elemarr
like this
I’ve used both to do speed tests.
In the second script I have to use arrays to store the coordinates, though.
select element is the slowest part in your code
it could be helpful to get the mesh representation of the UVs using channelinfo and get all the data from there
local meshUV = mesh mesh:(snapshotasmesh obj)
channelInfo.CopyChannel meshUV 3 mapch
channelInfo.PasteChannel meshUV 1 0
with redraw off
(
selectVertices = uv.unwrap.selectVertices
selectElement = uv.unwrap2.selectElement
getSelectedVertices = uv.unwrap.getSelectedVertices
getVertexPosition = uv.unwrap.getVertexPosition
for v in bitarr where bitarr[v] do
(
selectVertices #{v}
selectElement()
elem = getSelectedVertices()
minxy = [1e9,1e9]
for t in elem do
(
pt = getVertexPosition 0 t
if pt.x < minxy.x do minxy.x = pt.x
if pt.y < minxy.y do minxy.y = pt.y
)
-- uv.unwrap2.moveSelected [-minxy.x,-minxy.y,0]
bitarr -= elem
)
)
I’m not sure how to use the first part of your suggestion.
As for the second part, it looks nice (although I’m not sure how everything works), but I didn’t see a speed increase.
Do you think it’s possible to get away with not selecting any verts? Just working with the values in the bitarray and then moving them without selecting them?
Also get vert position seems slow.
I had a look at this:
https://help.autodesk.com/view/MAXDEV/2022/ENU/?guid=GUID-F1B3ECFD-2649-48C8-B130-15A17527433C
To my untrained eye it looks like they are manually growing the selection to select the UV islands. Maybe that’s faster? Or maybe just to avoid Unwrap modifier. I haven’t found a way to use the same method for Epolys anyhow.
Of course, it depends on the polycount of the model and the selection. And sometimes all you get with caching is removing memory leaks
Sure, forget about unwrap mod and its methods where possible and get all the needed data from the uv mesh object. I’m not sure if you can move or set unwrap verts position without selection.
It was long time ago when we dicussed how to improve things in uvtools. But making custom c++ methods to improve performance was the only way to go in the end…
this no longer helps in 2019+ versions. In early versions this was mostly a memory issue which could also affect performance.
we need a test scene to do correct performance tests… I suggest this:
modi =
(
max create mode
delete objects
sp = geosphere name:"test_mesh" segs:2 mapcoords:on
addmodifier sp (Uvwmap maptype:5)
sp = converttopoly sp
for k=1 to 3 do polyop.attach sp (copy sp)
update sp
modi = Unwrap_UVW()
addmodifier sp modi
max modify mode
modpanel.setcurrentobject modi
modi
)
as first step I can suggest the most simple way to get all uv clusters using built-in UVWUnwrap methods:
fn uvwElements0 modi: =
(
if modi == unsupplied do modi = modpanel.getcurrentobject()
if iskindof modi Unwrap_UVW do undo off with redraw off
(
disablerefmsgs()
modi.setTVSubObjectMode 1
elements = #()
verts = #{1..modi.numbervertices()}
done = #{}
for v in verts where not done[v] do
(
modi.selectvertices #{v}
modi.selectelement()
element = modi.getselectedvertices()
append elements element
join done element
)
enablerefmsgs()
elements
)
)
so we get:
(
gc()
t1 = timestamp()
m1 = heapfree
elements = uvwElements0 modi:modi
format "ELEMENTS0 elements:% time:% memory:% >> %\n" (try(elements.count) catch(-1)) (timestamp() - t1) (m1 - heapfree) elements
)
/*
#(#{1, 13..14}, #{2, 18, 43}, #{3, 47..48}, #{4, 56..57}, #{5, 65..66}, #{6, 74..75}, #{7, 92..93}, #{8, 100..101}, #{9, 108..109}, #{10, 116..117}, #{11, 37, 88}, #{12, 38..39}, #{15, 49..50}, #{16, 58..59}, #{17, 67..68}, #{19, 51..52}, #{20, 60..61}, #{21, 69..70}, #{22, 79..80}, #{23, 28, 87}, ...)
ELEMENTS0 elements:640 time:4996 memory:90368L >> #(#{1, 13..14}, #{2, 18, 43}, #{3, 47..48}, #{4, 56..57}, #{5, 65..66}, #{6, 74..75}, #{7, 92..93}, #{8, 100..101}, #{9, 108..109}, #{10, 116..117}, #{11, 37, 88}, #{12, 38..39}, #{15, 49..50}, #{16, 58..59}, #{17, 67..68}, #{19, 51..52}, #{20, 60..61}, #{21, 69..70}, #{22, 79..80}, #{23, 28, 87}, ...)
*/
the problem that is the Unwrap methods are slow… for example my c++ SDK implementation of the same task is:
count:640 time:10 heap:54120L
but we can do (collect clusters) with MXS, and it won’t be much slower than c++.
there is a thread on this forum about how best to find mesh geometry elements. The final algorithm can easily be used for uvw elements as well.
bad thing about unwrap if I remember correctly was a ‘dead’ vertex indexes after some operations like welding etc… it was complicating mapvert to poly/mesh index mapping