Notifications
Clear all

[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

40 Replies

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

Page 1 / 4