Fast attach algorithm


#21

Multithread in maxscript:

http://lonerobot.net/?p=50

Also since we are in on the topic,
I did something different to attach massive scenes,
I exported as obj, then imported back in with the “import as single mesh” option set.
(through script).


#22

Thanks for sharing that. The export to OBJ is a pretty cool way to “think outside the box” to get attaching done. Does OBJ export/import support more than one UVW channel? Also, material ID’s would probably be lost using that technique. But an option to definitely keep in mind.


#23

stigale: That’s a great idea… never thought of that :smiley: But time and memory wise is it worth it? Is it faster than doing a fast attach?

Dimentia: Sorry, I thought you knew the multithreaded dotnet code, there are lot of examples here on the forum and of course Lonerobot is the main reference for a snippet and detailed explanation.


#24

Yeah, the export as OBJ is very interesting. Although, u’d have to apply your materials again after, and like someone else said, u might lose your smoothing groups, but I’m not sure on that one.

Also, is it alot faster than just waiting for the attach?


#25

it’s much slower… also when we export as OBJ we convert all geometry to mesh format. Attaching of meshes is a lot faster than attaching of polys, but we are talking about polys.


#26

Yeah, but memory wise it might worth the shot. Dunno.


#27

Attaching of meshes is a lot faster than attaching of polys, but we are talking about polys.

Yeah, way faster. Example:


 try(destroydialog teaRoll)catch()
 
 rollout teaRoll "Roll"
 (
 spinner numPots "# Teapots: " type:#integer range:[1,500000,250]
 button pressMe "Cluster Attach"
 button pressMe2 "Linear Attach"
 button pressMe3 "Mesh Attach"
 
 progressbar prog1 "" value:0
 
 function makeTeapots total=
 (
 	teaArr = #()
 
 	posX = 0
 	posY = 0
 
 	for q in 1 to total do
 	(
 	posX += 10
 
 	if posX > 1000 then
 	(
 	posY += 50
 	posX = 0
 	)
 	teaArr[q] = teapot()
 	teaArr[q].pos.x = posX
 	teaArr[q].pos.y = posY
 	)
 
 	return teaArr
 )
 
 function clusterAttach objArr =
 (
 	j = 1
 	count = objArr.count
 
 	undo off
 	(
 	while objArr.count > 1 do
 	(
 	if classof objArr[j] != Editable_Poly then converttopoly objArr[j]
 
 	polyop.attach objArr[j] objArr[j+1]
 	deleteItem objArr (j+1)
 
 	j += 1
 
 	if (j + 1) > objArr.count then j = 1
 	)
 	)
 	return objArr[1]
 )
 
 function linearAttach objArr =
 (
 	converttopoly objArr[1]
 	undo off
 	(
 	for q in 2 to objArr.count do
 	(
 	prog1.value = (q/objArr.count as float)*100
 	polyop.attach objArr[1] objArr[q]
 	)
 	)
 	return objArr[1]
 )
 
 fn meshAttach objArr =
 (
 	--local myMesh = editable_mesh()
 	--for h=1 to objArr.count do attach myMesh objArr[h]
 		
 	j = 1
 	count = objArr.count
 
 	undo off
 	(
 	while objArr.count > 1 do
 	(
 	if classof objArr[j] != Editable_Mesh then converttomesh objArr[j]
 
 	attach objArr[j] objArr[j+1]
 	deleteItem objArr (j+1)
 
 	j += 1
 
 	if (j + 1) > objArr.count then j = 1
 	)
 	)
 	return objArr[1]
 )
 
 	on pressMe pressed do
 	(
 		delete $*
 		teaArr = makeTeapots numPots.value
 		tStart = timestamp()
 		clusterAttach teaArr
 		prog1.value = 0
 		tEnd = timestamp ()
 		print ("Attach finished. Total time: " + ((tEnd-tStart)/1000.0) as string + "s")
 	)
 
 	on pressMe2 pressed do
 	(
 		delete $*
 		teaArr = makeTeapots numPots.value
 		tStart = timestamp()
 		linearAttach teaArr
 		prog1.value = 0
 		tEnd = timestamp ()
 		print ("Attach finished. Total time: " + ((tEnd-tStart)/1000.0) as string + "s")
 	)
 	
 	on pressMe3 pressed do
 	(
 		delete $*
 		teaArr = makeTeapots numPots.value
 		tStart = timestamp()
 		meshAttach teaArr
 		prog1.value = 0
 		tEnd = timestamp ()
 		print ("Attach finished. Total time: " + ((tEnd-tStart)/1000.0) as string + "s")
 	)
 )
 createdialog teaRoll
 

Why can’t we just use mesh attach, and then convertToPoly the resulting mesh?


#28

you can try… to convert a big mesh to poly is very slow. it kills the method.


#29

Is there a modifier in max that brings all selected meshes together quickly?

Maybe a modifier would be the fastest, since its using the base SDK, and not going through maxscript?

Edit: Blob Mesh Modifier is actually somewhat close. Or the principal at least.


#30

Or, if you apply an EditPoly Modifier to something like 150 objects, its almost instant, then you can access the Geometry. But I’m not sure if/how that would help any.


#31

no… this is not sdk or not sdk issue. max just algorithmically does do multiple attaching wrong.
read this thread. it explains the problem…


#32

I understand that if you attach them in order, as the mesh grows, it gets exponentially slower when adding geometry to other geometry and rebuilding.

I’ve done tests of my own with attaching out of order, which helps, but still can be slow.
I’ve taken a look at the soulburn script that does this too.


#33

mini-challenge #3 was all about this problem… http://forums.cgsociety.org/showthread.php?f=98&t=982499&highlight=mini-challenge


#34

I needed to attach several thousand splines, so I modified Tyson’s cluster attach to support splines.
Thought this may be of use to someone else somewhere:


  --select the splines you want to attach, then run the script
  (
 	function splineAttach objArr =
 	(
 		j = 1
 		undo off
 		(
 			while objArr.count > 1 do
 			(
 				if j+1 > objArr.count then (j = 1) else 
 				(
 					convertToSplineShape objArr[j];  updateShape objArr[j]
 					convertToSplineShape objArr[j+1];  updateShape objArr[j+1]
 					addAndWeld objArr[j] objArr[j+1] 0.0;  updateShape objArr[j]
 					deleteItem objArr (j+1)
 					j += 1
 				)
 			)
 		)
 		return objArr[1]
 	)
 	local mySelection = selection as array
 	splineAttach mySelection
 )
  

*edit - missed a case


#35

why do you think this method faster than the simple linear one? did you compare?
fast attach algorithm works well only and specifically for editable polys, because of the way how poly update is implemented.


#36

why do you think this method faster than the simple linear one? did you compare?

I didn’t compare, just needed to attach several thousand splines and didn’t want to do it by hand. I was already familiar with this method, refactoring tyson’s function only took a few minutes. I am interested to see if a linear attach is faster. What conclusions do you have about linear vs. cluster spline attach?

*now that i’m pushing the fn, i see that it doesn’t scale well. tried to attach 10,201 circles and max went unresponsive. attaching 100 was no problem. guess i’ll code a linear spline attach instead…


#37

Ah, here is a much nicer spline attach fn:


--select the splines you want to attach, then run the script
  (
	 function splineAttach objArr =
	 (
		local myCopy = copy objArr[1];  convertToSplineShape myCopy;  delete objArr[1]
		for j=2 to objArr.count by 1 do
		(
			convertToSplineShape objArr[j]
			updateShape objArr[j]
			addAndWeld myCopy objArr[j] 0.0
			updateShape myCopy
		)
	 )
	 local mySelection = selection as array
	 splineAttach mySelection
 )

I’m not sure if the “updateShape objArr[j]” is necessary.
I’ll run some times on the fns and post comparisons.


#38

Linear spline attach for 100 circles: 253ms.
Linear spline attach for 1111 circles: 346905ms.

Cluster spline attach for 100 circles: 162ms
Cluster spline attach for 1111 circles: 10168ms.

Cluster spline attach is WAY faster.

Am I missing something, or is attaching splines slower than polys or meshes?

Code I’m using for comparisons:


 --select the splines you want to attach, then run the script
 (
 	(
 	source = ""
 	source += "using System;
"
 	source += "using System.Runtime.InteropServices;
"
 	source += "class WindowsGhosting
"
 	source += "{
"
 	source += " [DllImport(\"user32.dll\")]
"
 	source += " public static extern void DisableProcessWindowsGhosting();
"
 	source += "}
"
 
 	csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
 	compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
 
 	compilerParams.GenerateInMemory = on
 	compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)
 
 	assembly = compilerResults.CompiledAssembly
 	windowsGhosting = assembly.CreateInstance "WindowsGhosting"
 	windowsGhosting.DisableProcessWindowsGhosting()
 	)
 
 	local ts = timestamp()
 	local mem = heapfree
 	
 	 function splineAttach objArr =
 	 (
 		local myCopy = copy objArr[1];  convertToSplineShape myCopy;  delete objArr[1]
 		for j=2 to objArr.count by 1 do
 		(
 			convertToSplineShape objArr[j]
 			updateShape objArr[j]
 			addAndWeld myCopy objArr[j] 0.0
 			updateShape myCopy
 			if ((random 1 10) == 5) then (print (objArr.count-j))
 		)
 		return objArr.count
 	 )
 	
 	local mySelection = selection as array
 	 local total = splineAttach mySelection
 	
 	format "Time: %ms
" (timestamp()-ts)
 	format "Memory: %
" (mem-heapfree)
 	format "Splines Attached: %
" (total)
  )
   
 

     --select the splines you want to attach, then run the script
(
	  (
	source = ""
	source += "using System;
"
	source += "using System.Runtime.InteropServices;
"
	source += "class WindowsGhosting
"
	source += "{
"
	source += " [DllImport(\"user32.dll\")]
"
	source += " public static extern void DisableProcessWindowsGhosting();
"
	source += "}
"

	csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
	compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"

	compilerParams.GenerateInMemory = on
	compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source)

	assembly = compilerResults.CompiledAssembly
	windowsGhosting = assembly.CreateInstance "WindowsGhosting"
	windowsGhosting.DisableProcessWindowsGhosting()
	)

	local ts = timestamp()
	local mem = heapfree
	
	 function splineAttach objArr =
	 (
		 j = 1
		local theCount = objArr.count
		 undo off
		 (
			 while objArr.count > 1 do
			 (
				 if j+1 > objArr.count then (j = 1) else 
				 (
					 convertToSplineShape objArr[j];  updateShape objArr[j]
					 convertToSplineShape objArr[j+1];  updateShape objArr[j+1]
					 addAndWeld objArr[j] objArr[j+1] 0.0;  updateShape objArr[j]
					 deleteItem objArr (j+1)
					 j += 1
					if ((random 1 10) == 5) then (print (objArr.count))
				 )
			 )
		 )
		 return theCount
	 )
	
	 local mySelection = selection as array
	 local total = splineAttach mySelection
	
	format "Time: %ms
" (timestamp()-ts)
	format "Memory: %
" (mem-heapfree)
	format "Splines Attached: %
" (total)
 )
  
   
 

denis, what do you think of this?


#39

first of all i want to say that the testing of any tool/function/method for performance and memory usage is a very practice.
i don’t trust your result of the linear spline attaching of many nodes. it looks too slow for me with no reason. but… if the cluster method is faster we have to take it in account…
if you would like to make a test scene, we can organize a challenge.
i haven’t met a task of spline-shape massive attach before. if it’s a vital problem we can brainstorm it…


#40

Cluster Spline Attach


 Time: 883569ms
 Memory: -1777152L
 Splines Attached: 10201
 

I’m not even going to try the Linear attach for 10201 circles.
I would probably be waiting for a day or two.

There’s got to be a faster way to attach a # of splines greater than 10,000.
Or am I missing something here?