Notifications
Clear all

[Closed] MS + DotNet MultiThreading with objects creation

Hi,

I was wondering if it was possible to run a maxscript in multithreading with DotNet ( with BackgroundWorker ) and creating objects, moving, adding modifiers on them ?

I was trying with this, but it seems to crash max all the time :

myThreads = #()

fn doSomeStuff =
(
for i = 1 to 10000 do Box lengthsegs:1 widthsegs:1 heightsegs:1 length:10 width:10 height:10 

print "done"
)

for i = 1 to 8 do
(
myThreads[i] = dotNetObject "system.componentModel.backgroundWorker"
dotnet.addEventHandler myThreads[i] "DoWork" doSomeStuff
myThreads[i].runWorkerAsync()
)

Thank you for your help

30 Replies
1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

no. it’s not possible.

Well thank’s DenisT, i’ll try something else

I can be done indirectly… but you’ll loose any advantages of multi threading and it’s not very efficient but for small things it works great.

If you create a global array onto which you can push strings of maxscript from your backgroundworker thread you can execute those strings in the main thread via a execute command in dotnet timer like so:


 script_queue=#()
 
 fn scanScript = (
 
 	if (script_queue.count>0) do
 	(
 		scr=script_queue[1];
 		deleteItem script_queue 1
 		try(
 			execute scr[1]
 		) catch
 		(
 			print "**MXS ERROR**"
 		)
 	)
 )

scriptExecuter = dotNetObject "System.Windows.Forms.Timer"
 dotnet.addEventHandler scriptExecuter "tick" scanScript
scriptExecuter.interval = 100
scriptExecuter.start()
 

Thank you Jonadb, your solution seems to be for heavy calculation beside little maxscript viewport operation, is it ?

In fact my script run through all the vertex of an object ( and through the timeline) and it create a plane on each vertex ( with normal orientation), then i add on each modifiers and finally at the end of the frame it convert all the separate plane into a single edit poly. Then next frame is started.

I was thinking about cutting the timeline and then calling through DotNet 8 times 3ds max (1 per thread) and running the script, and finally regrouping the resulting geometry inside the same max.

2 Replies
(@jonadb)
Joined: 11 months ago

Posts: 0

I could be of use for heavy calculations with little output but ‘m using it for socket communications. A background thread receives maxscript commands via TCP/IP that needs to be executed in the main thread via above method.

One tip for combining a bazilion small object into one mesh… Abuse ProBoolean in ‘attach’ mode, it doesn’t alter geometry just attached everything into on big mesh very quickly.

Small example where $main if your base mesh


for obj in selection do
(
ProBoolean.SetOperandB $main obj 4 0
)

Then you can either collapse that to editmesh/poly or jsut leav it as proboolean object. I use this for a cloud script and it does 20000 planes in a few seconds.

(@gazybara)
Joined: 11 months ago

Posts: 0

Maybe I’m wrong but according to mxs help there is no addmethod 4


 <void>SetOperandA boolnode
 <void>SetOperandB boolnode node addMethod matMethod
 The third argument is  an integer controlling the operation to be performed and corresponding to the  radio button state in the UI. Possible values are: 
 
 
 0 -Union 
 
  1 -Intersection 
 
  2 -Subtraction 
 
  3 -Merge 
 
  The fourth argument  is an integer defining the method to add operands B, corresponding to the radio  button state in the UI. Possible values are: 
 
  0 -Reference 
 
  1 -Copy 
 
  2 -Move 
 
  3 -Instance 
 
  The fifth argument is  an integer controlling the material mdethod and corresponding to the radio  button state in the UI. Possible values are: 
 
  0 -Apply Operand Method 
 
  1 -Retain Original Material
 
 

Can you show a small example of your method.

Ok, thank you for the tips about the proboolean works great !

I keep searching about launching other 3ds max, opening the same scene , starting the same script but with other frame to perform.

Ps : I don’t get if you pass through TCP/IP to start things on the same computer or if you use it to start things through network

3 Replies
(@jonadb)
Joined: 11 months ago

Posts: 0

See this video for an early prototype of what I’m building in my spare time where the snippet is from: http://vimeo.com/47002666

With ‘shelllaunch’ you can start any program, including max.

Waht you could do is save your scene 8 times, each with a different name that includes a framerange but before you do that set a startupscript that extracts the frameranges from the filenames and starts the calculation for them after which it automatically saves the file.

So you get 8 files like this:

file_0_200.max
file_201_400.max
etc

Start Max via shelllaunch, each loading a different file… the startup script kicks in and performs the operation on set ranges and saves when done. Merge results into on file and you’re done

edit: sorry, not in a startup script directly but make a startup script that sets an ‘filePostOpen’ eventhandler which performs the calculations…

(@dangrover)
Joined: 10 months ago

Posts: 0

That’s amazing Jon, well done!

(@gazybara)
Joined: 11 months ago

Posts: 0

Yup, I agree with you. Very powerful method.

It’s a secret method But you can see them UI for ProBoolean, just count the radio button starting at 0, somehow 4 and 5 aren’t listen in the docs.

0=Union
1=Intersection
2=Subtraction
3=Merge
4=Attach <= this is what we want
5=Insert

Very nice. I did not know about that.
And how can I perform this “attach” method in this example if is not a secret


 delete objects
 shift = on
 tea = teapot radius:10 name:"main"
 for z = 0 to 9 do 
 (
 	shift = not shift
 	for x = 0 to 9 do 
 	(
 		main = (copy $main)
 		main.pos = if shift then [30*x,0,20*z] else [10+30*x,0,20*z]
 		in coordsys local main.rotation =  (random (eulerAngles 0 0 0) (eulerAngles 0 0 359))
 		main.wirecolor = (clr = random black white ; clr.s = 255. ; clr)
 	)
 )
 delete $main
 ProBoolean.SetOperandA $.main001
 -- . . . ???
 

Like so:


delete objects
 shift = on
 teamain = teapot radius:10 name:"teamain"
 
 ProBoolean.createBooleanObject  $teamain undefined 4 1 0   --create master boolean with no operant B (undefined)
 
 tea = teapot radius:10 name:"sub"
  
 for z = 0 to 9 do 
 (
	 shift = not shift
	 for x = 0 to 9 do 
	 (
		 sub = (copy $sub)
		 sub.pos = if shift then [30*x,0,20*z] else [10+30*x,0,20*z]
		 in coordsys local sub.rotation =  (random (eulerAngles 0 0 0) (eulerAngles 0 0 359))
		 sub.wirecolor = (clr = random black white ; clr.s = 255. ; clr)
		ProBoolean.SetOperandB $teamain sub 4 0   -- keep adding B operands
	)
 )

Hey Jonathan,
your attach method is very fast!
This is the test on the Teapot Wall
#1 TEST


 (
 delete objects
 shift = off
 geo = Editable_Mesh transform:(matrix3 1) wirecolor:yellow name:"teamain"	
 ProBoolean.createBooleanObject  $teamain undefined 4 1 0   --create master boolean with no operant B (undefined)
 tea = teapot radius:10 name:"sub"
 gc()
 t1 = timestamp()
 m1 = heapfree	
 for z = 0 to 19 do 
 (
 	shift = not shift
 	for x = 0 to 19 do 
 	(
 		sub = (copy $sub)
 		sub.pos = if shift then [30*x,0,20*z] else [10+30*x,0,20*z]
 		in coordsys local sub.rotation =  (random (eulerAngles 0 0 0) (eulerAngles 0 0 359))
 		ProBoolean.SetOperandB $teamain sub 4 0   -- keep adding B operands
 	)
 )
 delete $sub
 format "time:% memory:%
" ((timestamp()- t1 as float)/1000) (m1-heapfree)
 )
 

The result > time:0.328 memory:224184L

#2 TEST


 (
 delete objects
 shift = off
 geo = Editable_Mesh transform:(matrix3 1) wirecolor:yellow name:"teamain"	
 tea = teapot radius:10 name:"sub"
 gc()
 t1 = timestamp()
 m1 = heapfree	
 for z = 0 to 19 do 
 (
 	shift = not shift
 	for x = 0 to 19 do 
 	(
 		sub = (copy $sub)
 		sub.pos = if shift then [30*x,0,20*z] else [10+30*x,0,20*z]
 		in coordsys local sub.rotation =  (random (eulerAngles 0 0 0) (eulerAngles 0 0 359))
 		attach geo sub
 	)
 )
 delete $sub
 format "time:% memory:%
" ((timestamp()- t1 as float)/1000) (m1-heapfree)
 )
 

The result > time:24.684 memory:224184L

Huge difference in speed.
Thanks again for this example.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

you do wrong test for the performance…

the ProBoolean attach method is much slower then smart poly attach and much more expensive for memory use.

here is true test:


 fn makeNodes offset:[0,0,0] wirecolor:orange clean:on = 
 (
 	if clean do delete objects 
 	nodes = #()
 	for y=0 to 9 do for x=0 to 9 do
 	(
 		b = box width:10 length:10 height:10 widthSegs:10 lengthSegs:10 heightSegs:10 pos:([x*12,y*12,0] + offset) wirecolor:yellow
 		append nodes (converttopoly b)
 	)
 	s = box width:20 length:20 height:20 pos:([-20,0,0] + offset) wirecolor:wirecolor
 	#(converttopoly s, nodes)
 )
 
 fn clusterAttach source nodes =
 (
 	k = 1
 	insertitem source nodes 1
 	count = nodes.count
 	attach = polyop.attach
 	while nodes.count > 1 and not keyboard.escpressed do
 	(
 		attach nodes[k] nodes[k+1]
 		deleteItem nodes (k+1)
 		k += 1
 		if (k+1) > nodes.count do k = 1
 	)
 	nodes[1]
 )
 
 fn booleanAttach source nodes =
 (
 	ProBoolean.createBooleanObject source undefined 4 1 0
 	for k=1 to nodes.count while not keyboard.escpressed do
 	(
 		ProBoolean.SetOperandB source nodes[k] 4 0
 	)
 	converttopoly source
 	source
 )
 
 max create mode
 (
 	d = makeNodes offset:[0,0,25] wirecolor:brown clean:on
 	gc()
 	t1 = timestamp()
 	m1 = heapfree
 	booleanAttach d[1] d[2]
 	format "boolean >> time:% memory:%
" (timestamp() - t1) (m1 - heapfree)
 )
 (
 	d = makeNodes offset:[0,0,0] wirecolor:orange clean:off
 	gc()
 	t1 = timestamp()
 	m1 = heapfree
 	clusterAttach d[1] d[2]
 	format "cluster >> time:% memory:%
" (timestamp() - t1) (m1 - heapfree)
 )
 

Yeah I know

Page 1 / 2