Home Coding, Scripting, Shaders

[MAXSCRIPT] Stack selection affects speed. How to achieve same boost using script?

polycounter lvl 16
Offline / Send Message
dimwalker polycounter lvl 16

Hello, folks.

Doing a bunch of calculations on UV faces and script is pretty slow. What I've noticed is that it works reliably faster if I switch modifier stack from unwrap to one modifier below (epoly in my case). Tried to switch modifiers in script, but it gets confused if there are several unwraps in a row, so I would like to get same speed boost, but without messing around with modifier stack selection.

Tried disabling undo and enclosing main part in disableSceneRedraw() and enableSceneRedraw(), but that didn't help. What other things can I disable to imitate switching to epoly in modifier stack?

Here is stripped down part to demonstrate, it looks horrible and doesn't have all the torture UV faces are going through, but time difference is noticeable.


(
Teapot pos:[0.0,0.0,0.0] isSelected:on radius:6 segments:10
max modify mode
modPanel.addModToSelection (Uvwmap ()) ui:on
$.modifiers[#UVW_Map].maptype = 5
modPanel.addModToSelection (Edit_Poly ()) ui:on
modPanel.addModToSelection (Unwrap_UVW ()) ui:on
$.modifiers[1].edit()
subobjectLevel = 3


local uv = $.modifiers[1]

modPanel.setCurrentObject $.modifiers[1]
st = timestamp()
aaa = (uv.getselectedfaces()).count
for faceIndex = 1 to aaa do (
	uv.unwrap2.selectFaces #{faceIndex}
	uv.unwrap2.selectElement()
	faceNormal =  uv.getNormal (faceIndex - 1)
	myz = acos faceNormal.z
	uv.unwrap2.moveSelected [2,0,0]
)
format "unwrap selected time:%\n" (timestamp()-st)

modPanel.setCurrentObject $.modifiers[2]
st = timestamp()
aaa = (uv.getselectedfaces()).count
for faceIndex = 1 to aaa do (
	uv.unwrap2.selectFaces #{faceIndex}
	uv.unwrap2.selectElement()
	faceNormal =  uv.getNormal (faceIndex - 1)
	myz = acos faceNormal.z
	uv.unwrap2.moveSelected [2,0,0]
)
format "epoly selected time:%\n" (timestamp()-st)
)

Replies

  • monster
    Offline / Send Message
    monster polycounter

    Doing a bunch of calculations on UV faces and script is pretty slow. 

    Technically the calculations are very fast, it's changing selection and moving things inside a loop that is slow. I don't know the full application of your script, but this snippet I sped up with no loss of functionality.

    Before

    unwrap selected time:7909
    epoly selected time:4371
    

    After

    unwrap selected time:526
    epoly selected time:527
    

    Changes:

    • Store modifiers as variables
    • Store functions used in the loop as variables
    • Use UVXform to move the UVs (since they were all being moved the same amount).
    • No selection in the loop (there is almost always a way to do an action in script without a selection change).
    • You were selecting a face and then it's element. This can result in duplicated work. You can search online for a get uv elements function. If you need to iterate through elements.
    (
    	tObject = Teapot pos:[0.0,0.0,0.0] isSelected:on radius:6 segments:10
    	max modify mode
    	uwMod = Uvwmap()
    	epolyMod = Edit_Poly()
    	uvMod = Unwrap_UVW()
    	uvXform = UVW_Xform()
    	modPanel.addModToSelection (uwMod) ui:on
    	uwMod.maptype = 5
    	modPanel.addModToSelection (uvXform) ui:on
    	uvXform.U_Offset = 2
    	modPanel.addModToSelection (epolyMod) ui:on
    	modPanel.addModToSelection (uvMod) ui:on
    	modPanel.setCurrentObject uvMod
    	uvMod.edit()
    	subobjectLevel = 3
    	 
    	 
    	local uvUnwrap2 = uvMod.unwrap2
    	local uvGetNormal = uvMod.getNormal
    	 
    	modPanel.setCurrentObject uvMod
    	st = timestamp()
    	aaa = (uvMod.getselectedfaces()).count		
    	for faceIndex = 1 to aaa do
    	(
    		faceNormal = uvGetNormal (faceIndex - 1)
    		myz = acos faceNormal.z
    	)
    
    	format "unwrap selected time:%\n" (timestamp()-st)
    	 
    	modPanel.setCurrentObject epolyMod
    	st = timestamp()
    	for faceIndex = 1 to aaa do
    	(
    		faceNormal = uvGetNormal (faceIndex - 1)
    		myz = acos faceNormal.z
    	)
    
    	format "epoly selected time:%\n" (timestamp()-st)
    )
    
  • dimwalker
    Offline / Send Message
    dimwalker polycounter lvl 16

    Thank you.

    Some code here is a "placeholder", like moving elements. I need to move them to few different places and use "default" shift in this example just to have overall "weight" of calculations as close to what it will be in the end.

    Collecting elements in groups instead of moving each individually every iteration should help, will rewrite that part. Oh and definitely will shove everything in variables.

    As for UV element detection I found few methods. One is unavailable since it uses getMapFacesUsingMapVert and getMapVertsUsingMapFace meshops functions and polyops only has getMapFace, but nothing for mapverts or mapedges. I can probably detect element in similar way by running selection conversions in UV editor, but that would defeat the purpose by adding even more selection operations. Expanding to element from first available face in allFaces and excluding resulting array from allFaces seems like a good compromise. Example is for worst case scenario where each face is an element. but still runs expansion to element for reason described above. For now I'll try to move element collection outside of the main processing loop.

    If I missed your point, please give a link to dig through.

    Meanwhile, found that disableRefMsgs() enableRefMsgs() and suspendEditing() resumeEditing() pairs are making it faster so I'm using both.

    UPD: tried using getMapFacesUsingMapVert-getMapVertsUsingMapFace combo on snapshotted mesh. It is insignificantly faster in some cases and much slower in others. Another problem is that UV face IDs are different between mesh and poly and I suspect any time I saved on mesh processing would be lost if I try to transfer it back to poly. Fast way to get shells is a problem I can't solve.

  • monster
    Offline / Send Message
    monster polycounter

    Okay, sometimes you have to select in a loop, but the main issue in your original post was the duplicate work. You can take advantage of subtracting bit arrays to avoid the duplicated work.

    Check out these two functions below. The first takes 11 seconds on a default teapot and the second takes 1/10th of a second. The only difference is reducing the bitArray as you find the UV elements and using the where context in the loop to skip already removed polys.

    (
    	st = timeStamp()
    	(
    		uv = $.unwrapuvw
    		
    		pCount = uv.numberPolygons()
    		fArray = #{1..pCount}
    		uvElements = #()
    		
    		for i = 1 to pCount do
    		(
    			uv.selectFaces #{i}
    			uv.selectElement()
    			thisElement = uv.getSelectedFaces()
    			
    			append uvElements (thisElement)
    		)
    		
    		uv.selectFaces #{}
    		uvElements
    	)
    	format "unwrap selected time:%\n" (timestamp()-st)
    
    
    	st = timeStamp()
    	(
    		uv = $.unwrapuvw
    		
    		pCount = uv.numberPolygons()
    		fArray = #{1..pCount}
    		uvElements = #()
    		
    		for i = 1 to pCount where fArray[i] do
    		(
    			uv.selectFaces #{i}
    			uv.selectElement()
    			thisElement = uv.getSelectedFaces()
    			
    			append uvElements (thisElement)
    			fArray = fArray - thisElement
    		)
    		
    		uv.selectFaces #{}
    		uvElements
    	)
    	format "unwrap selected time:%\n" (timestamp()-st)
    )
    
    

    Output

    unwrap selected time:11473
    unwrap selected time:101
    
  • dimwalker
    Offline / Send Message
    dimwalker polycounter lvl 16

    Got it, thanks.

    Element selection is bothering me now =)

    Your hint about it being slower than calculation was right and sent me on a journey of poking around with element detection and reorganizing the whole thing. Expanding to element is slow, but does wonders with low amount of shells, say we have million polys unwrapped into just 2 shells. Meshop way works faster with worst case scenario up until ~50k shells, but then starts to decline and gets even slower than element expansion method.

    Native functions to works with UV shells would be perfect solution, but max still doesn't have it for some reason and now I'm wondering if there are other ways of element detection I just can't think of.

Sign In or Register to comment.