Notifications
Clear all

[Closed] a faster way for finding tiled UV's?

was up late last night scripting so my solution is probably not elegant & i feel like there’s an easier (and faster) way to do this…

i want to traverse selected objects in a scene and poll them to see if they have “tiled” uv’s – UV coordinates outside of the 0-1 range.

here’s my slow solution (really bogs down if theres a lot of meshes or high vert counts). is there a way of checking without determining each vert’s u & v coordinates directly? i just feel like i’m totally missing something simple & couldn’t find it in the docs…

for i in selection do
(			
	myUv = i.Unwrap_UVW --must have an unwrap UVW in the mod stack
	uvVerts = myUv.NumberVertices() --get the total number of UV verts
	vertCount = 0 --set counter to zero
	--loop through all the verts & increment the counter UNTIL one is found outside of the 0-1 range...
	for i in 1 to uvVerts while (((myUV.getVertexPosition 0 i).x > 0) and ((myUV.getVertexPosition 0 i).x < 1.01)) AND (((myUV.getVertexPosition 0 i).y > 0) and ((myUV.getVertexPosition 0 i).y < 1))
	do (vertCount = vertCount + 1)
	--compare the total number of verts to the counter number at the time the for loop ended
	if vertCount != uvVerts then (format "% has tiled UV's
" i.name) --if the numbers aren't equal, then apparently an "outside" vert was found 
)
8 Replies
  1. Faster UV coords check:
    Have a peek at “contains <box2> <point2>”.

Construct a simple Box2 once (’(Box2 0 0 1 1)’, I think), then test the points against that.
^^^ BAD IDEA ^^^ Box2’s only hold Integer values, making them entirely useless for a lot of things but definitely for UV coord checking.

  1. Faster loop:
    If you only need to know -if- there is a vertex outside the usual domain, then you can set a variable to true, and call ‘exit’ inside the test to exit the loop it is in prematurely. That way, for an object with 1,000 UV verts, if UV vert 1 is outside then you no longer have to check the 999 other UV verts.

thanks richard – you’re always a huge help on these forums.

i tried the box2 comparison & it’s definitely faster. on an average sized scene my original version of the script takes 12.32 seconds and the box2 comparison (below) takes 4.7 seconds!

for i in selection do
(			
	myUv = i.Unwrap_UVW --must have an unwrap UVW in the mod stack
	uvVerts = myUv.NumberVertices() --get the total number of UV verts
	vertCount = 0 --set counter to zero
	testBox = (Box2 0 0 1 1) --set up uv "test" for comparing uv coords to
	--loop through all the verts & increment the counter UNTIL one is found outside of the 0-1 range...
	for i in 1 to uvVerts while (contains testbox (myUV.getVertexPosition 0 i)) == true do
	(
		vertcount = vertcount +1
	)
	--compare the total number of verts to the counter number at the time the for loop ended
	if vertCount != uvVerts then (format "% has tiled UV's
" i.name) --if the numbers aren't equal, then apparently an "outside" vert was found 
)

i tried using an exit instead of a while statement in the for loop, but it was actually a teency bit slower (code below – maybe i’m not doing it right?). the maxscript help has a performance warning about how using exit is just an internal try/catch and that using while will be faster.

for i in 1 to uvVerts do
(
	if  (contains testbox (myUV.getVertexPosition 0 i)) != true then exit
	vertcount = vertcount +1
)

thanks again for the help – it did speed things up a bit!

yeah, YMMV with the continue/exit type calls – it’d do better on high-count meshes, and where the found verts are early in line, than on low-count meshes / verts near the end of the line.

if the while () works faster in your case, by all means use that
not sure if the boolean option vs the counter would still be faster, probably won’t matter much; for all I know you’ll want to adjust those verts later and you’d need to collect the indices anyway.

One more bit: ‘contains <box2> <point2>’ returns a boolean value, so no need for the “!= true” part, use a “not (contains etc.)” instead?
( again – not sure which is actually faster… the latter should be, as it’s just an inversion instead of a conditional, buuut… )

Edit: whoops. fixed.

It seems a bit daft, but I was wondering if there might be any value in copying the map channel of interest to the geometry channel, (temporarily, or in a copy of the object,) and then ‘just’ taking the bounding box of the resulting geometry? So far, though, my MXS attempting this is pretty filthy. ‘Just’ … because I’m imagining the code behind bounding box might be faster than the MXS loop…

Just to smack myself in the head some…

Don’t use the contains / Box2 method. It’s based on integers and will happily tell you that [-0.9,0.5] lays happily within (Box2 0 0 1 1) and thus fail miserably on most UV coordinates. I guess you could multiply all the values by a factor bajillion and call it ‘good enough’.

==========

Sadly that means going back to individual coordinates. drdubosc’s suggestion sounds worth a shot… saves looping over all the vertices. You’d have to check whether all the other calls are going to give too much overhead, however;


 with undo off (
	myNewNodes = #()
	maxOps.cloneNodes $ newNodes:&myNewNodes
	for o in myNewNodes do (
		convertToMesh o
		channelinfo.copyChannel o 3 1	-- copy UV channel 1
		channelInfo.pasteChannel o 1 0	-- paste into mesh channel
		bbox = nodeGetBoundingBox o o.transform -- get local BBox
		print bbox #nomap -- do something with the #(<point3>,<point3>) here
		-- you can ignore <point3>.z, unless you're dealing with uvW data
	)
	delete myNewNodes
)

turns out that the “copy map channel into mesh channel and measure bounding box values” is WAAAY faster. here’s the code snippet i used…

if ((bbox[1].x > 0) and (bbox[1].y > 0)) and ((bbox[2].x <= 1) and (bbox[2].y <=1)) then...

in my test scene of 127 objects, the vert comparison method (at the beginning of the thread) took 10.328 seconds, while the channel switching method took only .328 seconds!! – definitely the way to go.

one caveat tho – the vert compare method always returned 54 objects with out-of-range UV’s while the channel switch method always returned 59. turns out the 5 extra objects had in-range UV’s, but they needed their transforms re-set before testing. this was throwing the bounding box numbers out of whack.

thanks guys for the info – it’s been a huge help!

glad that worked out

great thinking, drdubosc

Yay! … and thanks, ZB, for writing it.

This b*!%!! MXS is like driving a car with 2 steering wheels, 1 for left, 1 for right … and 4 gearsticks … and the SDK looks even worse; mutter, mutter …