Notifications
Clear all

[Closed] Align UV script optimizations

I haven’t touched Unwrap UVW for a long time, but I remember that they “drastically improved” something about it, and the performance numbers I see for this example tell that something is still really broken.

I’m afraid this goes over my head for the moment.
Up until now I’ve been using a script in Blender, which is very fast, but as a 20+ years Max user it was a little annoying not being able to do it in Max.
I will look into some of the things you have mentioned. Thanks
If you have any brainwaves feel free to share

I just want to say that what you are doing (code above) is pretty reasonable, but the built-in methods and their representation in MXS is not very good. Which is not your fault.
The best way to improve performance is to do the same without Unwrap UVW using the POLY or MESH map methods.

Do I understand correctly that you want to align the bottom-left corner of the UV cluster’s bounding box to [0,0]?

… and to specify the uv cluster by geometry face or vertex selection?

I remember Serejah already found this topic once

Thanks to the developers of this site for an unmatched search engine!

Exactly.
The purpose is to be able to quickly texture thousands of low poly buildings (Blender-OSM plugin), which are at varying heights, so that the bottom of each building is at the bottom of the UV space. This gives a little more control and prevents any building from starting with a row of cut off windows at the base.
The left align prevents one side from having cut off windows, so better than nothing. The ability to select which islands to align is just nice to have, but not strictly necessary.

fn getMapElements node channel:1 = if iskindof node Editable_Poly or iskindof node.baseobject Editable_Poly do
(
	if polyop.getmapsupport node channel do
	(
		mesh = snapshotasmesh node
		if not iskindof node Editable_Poly do node = node.baseobject
		elements = #()
		
		done = #{}
		num = mesh.numfaces
		for f=1 to num while not keyboard.escpressed where not done[f] do 
		(
			last = -1
			faces = #{f}
			verts = #{}
			while faces.numberset != last and not keyboard.escpressed do
			(
				last = faces.numberset
				verts = meshop.getmapvertsusingmapface mesh channel faces
				faces = meshop.getmapfacesusingmapvert mesh channel verts
			)
			append elements verts
			join done faces
		)
		free mesh
		elements
	)
)

fn getMapElementBBox3 node tverts channel:1 = if iskindof node Editable_Poly or iskindof node.baseobject Editable_Poly do
(
	if polyop.getmapsupport node channel do
	(
		mesh = snapshotasmesh node

		bmin = [1e9,1e9,1e9]
		bmax = -[1e9,1e9,1e9]
		
		pp = for tv in tverts collect 
		(
			p = meshop.getMapVert mesh channel tv

			if p.x < bmin.x do bmin.x = p.x
			if p.y < bmin.y do bmin.y = p.y
			if p.z < bmin.z do bmin.z = p.z
				
			if p.x > bmax.x do bmax.x = p.x
			if p.y > bmax.y do bmax.y = p.y
			if p.z > bmax.z do bmax.z = p.z
				
			p
		)
		free mesh
		#(box3 bmin bmax, tverts, pp)
	)
)


fn alignMapElement node data channel:1 = if iskindof node Editable_Poly or iskindof node.baseobject Editable_Poly do
(
	if polyop.getmapsupport node channel do
	(
		-- data == #(box3 bmin bmax, tverts, pp)
		shift = data[1].min 
		k = 1
		for tv in data[2] do
		(
			p = data[3][k] - shift
			polyop.setmapvert node channel tv p
			k += 1
		)
	)
)


/****************************************************************************************/

sp = 
(
	max create mode
	delete objects
	sp = teapot name:"test_mesh" mapcoords:on
	addmodifier sp (Uvwmap maptype:0)
	sp = converttopoly sp
	for k=1 to 3 do polyop.attach sp (copy sp)
	update sp
	
	sp
)


clusters = getMapElements sp
data = for cluster in clusters collect (getMapElementBBox3 sp cluster)
for d in data do alignMapElement sp d 

(
	modi = Unwrap_UVW()
	addmodifier sp modi
	max modify mode
	modpanel.setcurrentobject modi
)

I hope I didn’t forget anything… of course, this can be algorithmically optimized, but already much faster than the built-in unwrap…

Very cool, I did a few tests and, according to those tests, this is about twice as fast as my previous script. The blender python script (uv align distribute) is still about 8x faster than this, which is pretty crazy. Maybe Blender has less overhead or something.

I’m not exactly sure what happens in this script, so I don’t know how to optimize it further, but I’ll take some time to go through it and learn.

It shouldn’t matter whether you’re using ms or python, right? They should be equally fast/slow within the Max environment?

1 Reply
(@serejah)
Joined: 11 months ago

Posts: 0

you can rewrite it so that you don’t have to make a separate mesh snapshot for each uv-element. If you have lots of elements it should definitely improve timings

...
fn getMapElementBBox3 node tverts channel:1 = if iskindof node Editable_Poly or iskindof node.baseobject Editable_Poly do
(
	if polyop.getmapsupport node channel do
	(
		mesh = snapshotasmesh node

maxscript has not very good loop iteration performance and you’re bound to use high level abstractions, so on large polycounts mxs will lose to python for sure. For python you can try MaxPlus or whatever came as a replacement in newer max versions to improve timings. But you’ll have to use c++ sdk sources to learn how it all works under the hood. Which can be overwhelming
In general x10 speedup can be achieved depending on the task if you switch to low level implementation with c++ or c#.

here’s what I suggested doing with uv-mesh representation
don’t know why but subsequent execution of this code doing twice as fast than the first run after max start


t1=timestamp();hf = heapfree
max create mode
with redraw off
(
	
-- example scene
sp = 
(
	max create mode
	delete objects
	sp = teapot name:"test_mesh" mapcoords:on isSelected:true
	addmodifier sp (Uvwmap maptype:0)
	sp = converttopoly sp
	for k=1 to 3 do polyop.attach sp (copy sp)
	update sp
	
	sp
)




fn GetAllMeshElements node =
(
	local tmesh = snapshotasmesh node
	local faces = #{1..tmesh.numfaces}
	local verts = for v = 1 to tmesh.numverts collect #()
	local undone = #{1..tmesh.numverts}
	local elements = #()
	
	for j in faces do
	(
		f = getface tmesh j

		append verts[f[1]] j
		append verts[f[2]] j
		append verts[f[3]] j		
		
	)

	for i in faces do
	(		
		element = #(i)
		for j in element where faces[j] do
		(
			faces[j] = false
			f = getface tmesh j
			
			if undone[f[1]] do (join element verts[f[1]]; undone[f[1]] = false )
			if undone[f[2]] do (join element verts[f[2]]; undone[f[2]] = false )
			if undone[f[3]] do (join element verts[f[3]]; undone[f[3]] = false )
			
		)		
		append elements (element as bitarray)
	)
	
	elements
)

sel = selection[1]
convertToMesh sel

tri = snapshotasmesh sel
meshUV = mesh mesh:tri
channelInfo.CopyChannel meshUV 3 1
channelInfo.PasteChannel meshUV 1 0
collapseStack meshUV

elems = GetAllMeshElements meshUV

meshop_getVertsUsingFace = meshop.getVertsUsingFace

vts = #()
vts.count = tri.numverts
for el in elems do
(
	fv = meshop_getVertsUsingFace meshUV el
	
	shift = [ 1e9, 1e9, 0 ]
	
	for v in fv do
	(
		vts[v] = vv = getVert meshUV v
				
		if vv.x < shift.x do shift.x = vv.x
		if vv.y < shift.y do shift.y = vv.y
	)
	
	for v in fv do setvert meshUV v (vts[v] - shift)
		
)

update meshUV

channelInfo.CopyChannel  meshUV 1 0
channelInfo.PasteChannel sel 3 1
delete meshUV
free tri
)
	

format "Time: %sec. Mem: %\n" ((timestamp()-t1)/1000 as float) (hf-heapfree)
Page 2 / 4