Notifications
Clear all

[Closed] Groups break and weld functions – optimizing

I have two function I use in my game meshes exporter.

First one is breaking given set of vertices and returns groups of “child” vertices peer each “source” one.
So i.e. you breaking one vert, then 3 new verts are created. Function returns all these 4 verts as one group.

Second function is oposite to one I descripted above. It welding given groups of vertices.
I.e you giving two sets of vertices, they will be welded into two verts (one for each set).

Everythink works correctly, but weldGrups function is very slow. Actually I no using standard max’s weld option, but change vert indices in faces. It’s becouse of if you weld some verts then some-1 verts are disapearing. Then vertice indices are updated and I had to update also indices in weldGroups input parameter. Now it’s much faster, but still to slow.
On my PC weldGroups execution for 10 segments teapot takes 55 seconds:argh:
I spend lot time on it, but I have no idea how to optimize it.

Code comes with testing interface, so you can run it. Input geometry should looks same as before, without smoothing changes or moved faces.

-- groups breakw/weld functions
 
 if( TestFloater != undefined ) do closerolloutfloater TestFloater
 
 function getFacesByVert object vert = 
 (	
 	local faces_val = meshOp.getFacesUsingVert object #{vert}
 	local faces = #()
 
 	for i=1 to faces_val.count do
 	(
 		if( faces_val[i] ) then
 			append faces i
 	)
 
 	faces -- return
 )
 
 function getFacesByVerts object verts = 
 (	
 	local faces_val = meshOp.getFacesUsingVert object verts
 	local faces = #()
 
 	for i=1 to faces_val.count do
 	(
 		if( faces_val[i] ) then
 			append faces i
 	)
 
 	faces -- return
 )
 
 function weldGroups mesh wGroups =
 (
 	local face, vFaces, v, con
 	for i=1 to wGroups.count do
 	(
 		if( wGroups[i].count > 1 ) then -- least two verts to weld
 		(
 			--sort wGroups[i] -- ascending order
 
 			for j=2 to wGroups[i].count do
 			(
 				vFaces = getFacesByVert mesh wGroups[i][j]
 
 				for k=1 to vFaces.count do
 				(
 					face = getFace mesh vFaces[k]
 
 					con = true -- continue loop
 					for v=1 to 3 while con do -- face's vertices
 					(
 						if( face[v] == wGroups[i][j] ) then -- found the corner vertex that will be weld
 						(
 							face[v] = wGroups[i][1] -- set idx of first vertex in wg (all vertices are like target weld to it)
 							setFace mesh vFaces[k] face -- update face in mesh
 							con = false -- break loop
 						)
 					)
 				)
 			)
 		)
 
 		if( (TestRoll.pBar != undefined) and ((mod i 50) == 0) ) then TestRoll.pBar.value = (i*50.0/wGroups.count) -- debug progress bar (call one peer 50 times)
 	)
 	
 	meshOps.removeIsolatedVerts mesh
 )
 
 function breakToGroups mesh verts =
 (
 	local faces_indices = getFacesByVerts mesh verts
 	local faces = for i=1 to faces_indices.count collect (getFace mesh faces_indices[i])
 	breakGroups = for i=1 to mesh.numverts collect #(i)
 	
 	meshop.breakVerts mesh verts -- break
 	
 	local face, idx
 	for i=1 to faces.count do
 	(
 		face = getFace mesh faces_indices[i] -- get coresponding face
 		
 		for j=1 to 3 do 
 		(	
 			idx = findItem breakGroups[faces[i][j]] face[j] -- check this verticew isn't already added
 			
 			if( idx == 0 ) then
 				append breakGroups[faces[i][j]] face[j]
 		)
 	)
 
 	breakGroups -- return
 )
 
 -- testing interface
 if( TestRoll != undefined ) then -- recreate if window already existing
 	DestroyDialog TestRoll
 
 rollout TestRoll "Group Break/Weld testing" 
 (
 	button getBtn "S-T-A-R-T" width:280
 	progressbar pBar width:280
 
 	on getBtn pressed do
 	(
 		undo off
 		(
 			--disableSceneRedraw()
 
 			if( selection.count > 0 ) then
 			(
 				--obj = snapshotAsMesh selection[1]
 				obj = convertToMesh selection[1] -- result check in scene
 				
 				-- break all vertices into groups
 				local groups = (breakToGroups obj obj.verts)
 				print( "Break\Weld groups: " + (groups as string) )
 
 				pBar.color = red
 				local t = timeStamp()
 
 				--weldGroups obj groups -- TEST FUNCTION
 				weldGroups obj.mesh groups -- TEST FUNCTION, result check
 				
 				-- indicate done
 				pBar.color = green
 				pBar.value = 100
 				getBtn.text = ("WeldGroups execute time: " + (((timeStamp()-t)/1000.) as string) + "s")
 				print getBtn.text--*/
 
 				redrawViews()
 			)
 			else
 				messageBox "No selected!"
 
 			--enableSceneRedraw()
 		)
 	)
 )
 
 -- create the rollout window and add the rollout
 CreateDialog TestRoll width:300
5 Replies

to make the correct welding you don’t need to recompute all vertex “groups” after every weld operation.

Try to understand the logic:

  1. You have a group of vertices to weld (say #(4, 19,126, 148)). After successful welding only vertex with lowest number (4) will stay. Higher numbers (19,126,148) will go away.
  2. This operation will not effect any vertices of the mesh with indexes lower then 4.
  3. All vertices with indexes higher then 19 will loose 1 index
  4. All vertices with indexes higher then 126 will loose 1 index more
  5. All vertices with indexes higher then 148 will loose 1 index more

all that you need is recalculate vertex indexes for all weld “groups” next to welded one:

the pseudo code is:
for k=1 to groups.count do
(
weld group[k]
[i] for i=k+1 to groups.count do recalculate groups
)

Yes Denis, exacly that I had doing:

Group indices updating function:

function removeWeldedVerts verts welded =
  (
  	if( (welded.count > 1) and (verts.count > 1) ) then -- weld needs least two vertices
  	(
  		local con -- for break loops
  
  		--sort verts -- ascending order
  		--sort welded
  
  		for i=1 to verts.count do
  		(
  			con = true
  			for j=welded.count to 1 by -1 while con do
  			(
  				if( verts[i] > welded[j] ) then
  				(
  					verts[i] -= (j-1) -- how many vertices before this vertice has disapear
  					con = false -- break loop
  				)
  				else
  					verts[i] = welded[1] -- this vertice is in group of welded, so after weld it will has idx of first in group
  			)
  		)
  	)
  
  	verts -- return
  )

And groupsWelding code:

local welded = #() -- welded vertices
  for i=1 to weldGroups.count do
  (
  	removeWeldedVerts weldGroups[i] welded -- correct indices in array that will be weld
  
  	meshOp.weldVertSet mesh weldGroups[i] -- weld the group
  		
  	join welded weldGroups[i] -- join just welded to welded vertices array
  	sort welded -- required for correct working of removeWeldedVerts
  
  	--ProgressDialog.setCurrProgress (i*1000/weldGroups.count) -- my own progress dialog script
  )--*/

It is slower than code I posted before.

you are doing not what exactly the same as i said…

the code requires sorted vertex index arrays:


   for k=1 to weld_groups.count do
   (
   	  group = weld_groups[k]
   	 
   	  for i=k+1 to weld_groups.count do 
   	  (
   			next = weld_group 
			n = 1
 			while n < group.count do 
			(
					 n += 1
					 for j=1 to next.count where next[j] < group[n] do next[j] -= 1
			)
   	  )
   )
   
   -- after that:
   for g in weld_groups where g.count > 1 do [b]<weld g>[/b]
   

ps. i’m writing from my cell phone and can’t check the code. also i’m sure that my code might be optimized very well.

You code isn’t finished, but I tried gues what you was thinking about and I created this:

for i=1 to (wGroups.count-1) do
 (
 	group = wGroups[i]
 
 	for j=group.count to 1 by -1 do
 	(
 		for k=i+1 to wGroups.count do
 		(
 			next = wGroups[k]
 
 			for m=next.count to 1 by -1 do
 			(
 				if( group[j] < next[m] ) then
 					next[m] -= 1
 				else
 				if( group[j] == next[m] ) then
 						next[m] = group[1]
 			)
 		)
 	)
 
 	if( TestRoll != undefined ) then 
 		TestRoll.pBar.value = (i*100.0/wGroups.count) -- debug progress bar
 )

But it needs more than 4 minutes to execute

Later I created this:

function removeWeldedVerts verts welded =
 (
 	if( (welded.count > 1) and (verts.count > 1) ) then -- weld needs least two vertices
 	(
 		if( verts[verts.count] > welded[1] ) then
 		(
 			if( verts[1] < welded[welded.count] ) then
 			(
 				local i = welded.count
 				local wVerts = welded.count
 				
 				for j=verts.count to 1 by -1 where (i>0 and verts[j]>welded[1]) do
 				(
 					while( verts[j] < welded[i] ) do i -= 1
 					verts[j] -= i
 				)
 			)
 			else
 			(
 				for i=1 to verts.count do
 					verts[i] -= welded.count
 			)
 		)
 	)
 )

and welding function:

function weldGroups mesh wGroups =
 (
 	local welded = #(), lastWelded
 	for i=1 to wGroups.count do
 	(
 		sort wGroups[i]
 		lastWelded = #(); join lastWelded wGroups[i] -- copy the group
 		
 		removeWeldedVerts wGroups[i] welded -- update indices
 		meshOp.weldVertSet mesh wGroups[i] -- weld the group
 		
 		deleteItem lastWelded 1 -- delete first vert (it wasn't deleted when weld)
 		join welded lastWelded -- add last welded to welded group
 		sort welded
 
 		if( TestRoll != undefined ) then 
 			TestRoll.pBar.value = (i*100.0/wGroups.count) -- debug progress bar
 	)
 )

It takes 15 seconds (10 segments teapot). I hope it can be still optimized.

I don’t know how to create independend copy of array. I done it that way:

lastWelded = #(); join lastWelded wGroups[i] -- copy the group

but should be better solution

if you wanna copy an array (or any value) you can use the ‘copy’ function for nested stuff use the ‘deepCopy’ function