Mini-Challenge #3


#52

I wish you could SnapShotAsMesh a group of objects, and get that as TriMesh, then assign that to a EMesh or EPoly…


#53

Yeah, I tried that in about 5 different ways, no go. :frowning:


#54

You might be able to get all vert positions in the objects and create a new mesh from that, but then you won’t get UVs and Textures, and Smoothing, ect… So prob. not worth it, heh. :banghead:


#55

i tested, not help


#56

too bad…


#57

I tried to go multithreded but ran into some problems…
If I run it with more then 1 thread max crashes and it’s very unstable as it is with 1.
I saw some discussions about the backgroundworker where it was mentioned that some mxs functions won’t work with it, I guess attach is one of them.
here is my attempt:

	struct progUpdater (
		count,
		current = 0,
		pb,
		
		fn increment =
		(
			current += 1
			pb.value = 100 * current / count
		)
	)
	
	global pbManager
	
	struct hcNode (
		val = 0,
		left,
		right,
		obj,
		count = 1,
		verts = 0,
		
		fn init =
		(
			if obj != undefined then
				val = obj.numVerts
			else (
				if left != undefined then (
					val += left.val
					val += right.val
					verts = left.verts + right.verts + left.val + right.val
					count = left.count + right.count
				)
			)
		),
		
		fn mergeChildren =
		(
			local att = polyop.attach
			
			if left != undefined then (
				local a = left.mergeChildren() 
				local b = right.mergeChildren()
				left = undefined
				right = undefined
				att a b
				obj = a
				verts += val
			) else
				pbManager.increment()
			
			obj
		),
		
		start = init()
	)
	
	fn sortByVertNum a b =
	(
		a.val - b.val
	)
	
	fn insertInPlace arr new start: end: =
	(
		if end - start < 0 then
			append arr new
		else if end - start == 0 then (
			if arr[start].val > new.val then
				insertItem new arr start
			else
				insertItem new arr (start + 1)
		) else (
			local i = amin (start + (end - start) / 2) (end - 1)
			
			if arr[i].val > new.val then
				insertInPlace arr new start:start end:i
			else if arr[i].val < new.val then
				insertInPlace arr new start:(i + 1) end:end
			else
				insertItem new arr i
		)
	)
	
	struct treeMonitor (
		roots,
		next = 0,
		count = 0,
		source,
		occupied = false,
		
		fn getRoot =
		(
			next += 1
			occupied = false
			roots[next]
		),
		
		fn mergeRoots =
		(
			local att = polyop.attach
			
			for o in roots do
				att source o.obj
		),
		
		fn done =
		(
			count += 1
			if count == roots.count then 
				mergeRoots()
			redrawViews()
		)
	)
	
	global hcMergeTreeFN
	fn hcMergeTreeFN =
	(
		global hcTreeMonitor
		if hcTreeMonitor != undefined then (
			hcTreeMonitor.occupied = true
			local root = hcTreeMonitor.getRoot()
			root.mergeChildren()
			hcTreeMonitor.done()
		)
	)
	
	fn foo source nodes pb:pb1 =
	(
		local numThreads = 1 --sysInfo.cpuCount
		local nodeArr = for o in nodes collect hcNode obj:o
		qsort nodeArr sortByVertNum
		
		while nodeArr.count > numThreads do (
			local left = nodeArr[1]
			local right = nodeArr[2]
			deleteItem nodeArr 2
			deleteItem nodeArr 1
			local new = hcNode left:left right:right
			insertInPlace nodeArr new start:1 end:nodeArr.count
		)
		
		local count = 0
		for o in nodeArr do (
			count += o.count
			verts += o.verts
		)
		pbManager = progUpdater count:count pb:pb1
		
		/*
		local att = polyop.attach
		for o in nodeArr do (
			o.mergeChildren()
			att source o.obj
		)
		*/
		
		global hcTreeMonitor = treeMonitor roots:nodeArr source:source
		
		global hcMergeWorkers = #()
		for i = 1 to numThreads do (
			local newWorker = dotnetObject "System.ComponentModel.BackGroundWorker"
			dotNet.addEventHandler newWorker "DoWork" hcMergeTreeFN
			append hcMergeWorkers newWorker
			while hcTreeMonitor.occupied do ()
			newWorker.RunWorkerAsync()
		)
	)
	
	
	fn customAttach source nodes pb:pb1 = 
	(
		count = nodes.count
		
		foo source nodes pb:pb1
		
		source
	)

#58

I found that every operation that affects scene nodes crashes max, when used in a backgoundworker.


#59

what if we could attach the trimesh values of the objects in a backgroundworker and when they are done create a new poly from that trimesh value?


#60

I’ve tried going down this route, but I could not get meshop.attach to successfully attach two trimeshes without one of them being a node. Is it possible?

edit: scratch that, I was operating on the mesh itself and not a copy of it.


#61

found a way! (but still not so stable)

	struct progUpdater (
		count,
		current = 0,
		pb,
		
		fn increment =
		(
			current += 1
			pb.value = 100 * current / count
		)
	)
	
	global pbManager
	
	struct hcNode (
		val = 0,
		left,
		right,
		obj,
		count = 1,
		verts = 0,
		
		fn init =
		(
			if obj != undefined then
				val = obj.numVerts
			else (
				if left != undefined then (
					val += left.val
					val += right.val
					verts = left.verts + right.verts + left.val + right.val
					count = left.count + right.count
				)
			)
		),
		
		fn mergeChildren =
		(
			local att = meshop.attach
			
			if left != undefined then (
				local a = left.mergeChildren() 
				local b = right.mergeChildren()
				left = undefined
				right = undefined
				att a b
				obj = a
				verts += val
			) else
				pbManager.increment()
			
			obj
		),
		
		start = init()
	)
	
	fn sortByVertNum a b =
	(
		a.val - b.val
	)
	
	fn insertInPlace arr new start: end: =
	(
		if end - start < 0 then
			append arr new
		else if end - start == 0 then (
			if arr[start].val > new.val then
				insertItem new arr start
			else
				insertItem new arr (start + 1)
		) else (
			local i = amin (start + (end - start) / 2) (end - 1)
			
			if arr[i].val > new.val then
				insertInPlace arr new start:start end:i
			else if arr[i].val < new.val then
				insertInPlace arr new start:(i + 1) end:end
			else
				insertItem new arr i
		)
	)
	
	struct treeMonitor (
		roots,
		next = 0,
		count = 0,
		source,
		occupied = false,
		
		fn getRoot =
		(
			next += 1
			occupied = false
			roots[next]
		),
		
		fn mergeRoots =
		(
			local att = meshop.attach
			
			for o in roots do
				att source o.obj
			
			source = mesh mesh:source
			convertTo source Editable_Poly
		),
		
		fn done =
		(
			count += 1
			if count == roots.count then 
				mergeRoots()
			redrawViews()
		)
	)
	
	global hcMergeTreeFN
	fn hcMergeTreeFN =
	(
		global hcTreeMonitor
		if hcTreeMonitor != undefined then (
			hcTreeMonitor.occupied = true
			local root = hcTreeMonitor.getRoot()
			root.mergeChildren()
			hcTreeMonitor.done()
		)
	)
	
	fn foo source nodes pb:pb1 =
	(
		local numThreads = 10 --sysInfo.cpuCount
		local nodeArr = #()
		for o in nodes do (
			append nodeArr (hcNode obj:(snapshotAsMesh o))
			delete o
		)
		qsort nodeArr sortByVertNum
		
		while nodeArr.count > numThreads do (
			local left = nodeArr[1]
			local right = nodeArr[2]
			deleteItem nodeArr 2
			deleteItem nodeArr 1
			local new = hcNode left:left right:right
			insertInPlace nodeArr new start:1 end:nodeArr.count
		)
		
		local count = 0
		for o in nodeArr do (
			count += o.count
			verts += o.verts
		)
		pbManager = progUpdater count:count pb:pb1
		
		global hcTreeMonitor = treeMonitor roots:nodeArr source:(snapshotAsMesh source)
		delete source
		
		for i = 1 to numThreads do (
			local newWorker = dotnetObject "System.ComponentModel.BackGroundWorker"
			dotNet.addEventHandler newWorker "DoWork" hcMergeTreeFN
			while hcTreeMonitor.occupied do ()
			newWorker.RunWorkerAsync()
		)
		
		hcTreeMonitor.source
	)
	
	fn customAttach source nodes pb:pb1 = 
	(
		count = nodes.count
		
		foo source nodes pb:pb1
	)

#62

Interestingly enough, about 60% of the execution time when doing the whole thing with meshes, is the conversion back to poly in the end.


#63

I’ve managed to get some scene interaction with background worker, by disabling ref messages.
Perhaps this could be used towards the attach algorithm.

Example:


workers = #()

fn createBox = box()

fn complete s e =
(
	deleteItem workers (findItem workers s)
	if workers.count==0 do enablerefmsgs()
)

disablerefmsgs()

for i = 1 to 8 do
(
	local worker = dotnetObject "System.ComponentModel.BackGroundWorker"
	dotNet.addEventHandler worker "DoWork" createBox
	dotnet.addeventhandler worker "RunWorkerCompleted" complete
	append workers worker	
	worker.RunWorkerAsync()
)

result: 8 boxes created, max doesn’t crash :slight_smile:


#64

very cool, I will test this with modifier operations…


#65

Seems this is still quite unstable and doesn’t work for all commands.


#66

i think that any max scene change or max UI change can be done in the same as application thread only. that means there is no way to do these operations using multithreading.


#67

Here is a time to show my variant of fast attach. I call it “Caster” (don’t ask me why).
My idea is to have as less as possible number of updates. The ideal algorithm has to attach every time the nodes with minimum number of vertices. So we have to sort the node array every time before attach.
QSORT is very slow if we do it for every iteration of the loop. I’m using .net Sort (sorting second array using first as keys).
Also I don’t use deleteItem (which is also slow for big arrays).
I’m using GetHandleByAnim (to make .net array) and GetAnimByHandle (to speed up access to node’s pointer in an array).

As you can see my algorithm works much faster in situation where we have to attach small boxes to one high-poly box. Without sorting the cluster method has the similar problem as the max Linear method.

check the code and play with parameters…


#68

Here is an optimized version of my version without the multithreading stuff.
It does the same thing Denis’s casterAttach method do but without the need of sorting the array (with or without dotnet’s help) every time and it looks like it works at the same speed if not a bit faster then the caster method.

The idea is taken from the Huffman code algorithm. At first I sort the objects once by vertex count, then I take the first 2 objects in the sorted array, attach them and delete them from the array. Now I use the insertInPlace function to insert a new object created from the 2 attached objects in it’s correct place in the array using a binary search technique. I repeat these steps until the array holds only 1 object. I also make sure I remember which object was originally the source to make sure it doesn’t get deleted.

Anyhow Denis’s method is still very cool and I learned some new stuff from it, tanks for that!

	fn sortByVertNum a b =
	(
		a.val - b.val
	)
	
	struct s_Node (
		obj,
		val,
		isSource = false,
		
		fn init =
		(
			val = obj.numVerts
		),
		
		start = init()
	)
	
	fn insertInPlace arr new start: end: =
	(
		if end - start < 0 then
			append arr new
		else if end - start == 0 then (
			if arr[start].val > new.val then
				insertItem new arr start
			else
				insertItem new arr (start + 1)
		) else (
			local i = amin (start + (end - start) / 2) (end - 1)
			
			if arr[i].val > new.val then
				insertInPlace arr new start:start end:i
			else if arr[i].val < new.val then
				insertInPlace arr new start:(i + 1) end:end
			else
				insertItem new arr i
		)
	)
	
	fn huffmanAttach source nodes pb:pb1 =
	(
		nodes = for o in nodes collect s_Node obj:o
		append nodes (s_Node obj:source isSource:true)
		qsort nodes sortByVertNum
		local att = polyop.attach
		local n = nodes.count
		
		while nodes.count > 1 do (
			if nodes[2].isSource then
				swap nodes[1] nodes[2]
			att nodes[1].obj nodes[2].obj
			local newNode = s_Node obj:nodes[1].obj isSource:nodes[1].isSource
			deleteItem nodes 2
			deleteItem nodes 1
			insertInPlace nodes newNode start:1 end:nodes.count
			
			pb.value = 100.0 * (1.0 - 1.0 * nodes.count / n)
			verts += newNode.val
		)
	)
	
	fn customAttach source nodes pb:pb1 = 
	(
		count = nodes.count
		
		huffmanAttach source nodes pb:pb1
		
		source
	)

#69

well… the Party Of Math Lovers won. :slight_smile:
http://forums.cgsociety.org/showthread.php?f=98&t=985724


NEW LOOK of this site. Do you like it?
#70

wrong place for the post


#71

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.