I lied. One more script – then I’m done
Here is a script that uses a dotNet timer to run a background worker thread every 2 seconds.
Because the thread executes quickly, max does not crash. But, since the thread is controlled by a timer, the thread can execute again and again. Run the script and watch as boxes magically appear in the viewport every two seconds. You can pan, rotate, zoom, etc… while the background thread creates the boxes. Once 20 boxes have been created, the script stops.
( --background box builder
clearListener()
maxScriptString = @"myBox = box(); myBox.pos = [(random -200 200),(random -200 200),0]"
fn bkgWrkr =
( with redraw off ( execute maxScriptString); completeRedraw(); )
MainThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
MainThread.WorkerSupportsCancellation = true
dotNet.addEventHandler MainThread "DoWork" bkgWrkr
--control the bkgWrkr with a .Net timer
local myCount = 1; local maxCount = 20
ftm = dotnetobject "System.Windows.Forms.Timer"
ftm.Interval = 2000 -- every two seconds
fn onTick s g =
(
print myCount
if myCount < maxCount then
(
if MainThread.isBusy == false then
(
MainThread.RunWorkerAsync(); myCount += 1
)
) else (
ftm.Stop(); dotnet.removeAllEventHandlers ftm; ftm.Dispose()
)
)
dotnet.addEventHandler ftm "Tick" onTick
ftm.Start()
)
(based on code by denisT and loneRobot)
Thats really cool! Works fine for me, no crashes on max 2010 64bit. I def. gotta mess with this more
Very nice, thanks for doing the leg work on this one. This was on my long list of toDos for my joy stick system. I want to see if I can get the dialog to update without affecting the speed of Max.
Worked here on my laptop in Max 2011 32bit without error. Will test some more.
I think this script is almost complete as well. Just make it make a pile or teapots any where you click your mouse and then add it into a menu item in the main menu bar that is called “Don’t Press This”. Release it into production and watch the fun begin.
http://matthewlichy.com/backgroundWorker/backgroundWorker.html
Modified it to work on Verts on a Mesh. Seems to work just fine
I have not tried it yet. So why does the Max command need to be in a string?
I have no idea. Maybe something with the background worker having trouble with multiple entries in the function, so instead it’s 1 string? TheGrak should know why I suppose.
Hmmm sorry if I sound stupid but I always thought this would work and was the default behaviour… The only thing that I know it was messy was the creation of “stuff” in the background worker thread…
So why does the Max command need to be in a string?
TheGrak should know why I suppose.
The max command doesn’t need to be in a string. Sending a maxscript as a string to the bkgWrkr seemed to be the most modular way of testing different kinds of code. I think it does need to be in a with redraw off ( ) scope context, and the less time the function takes to complete, the less likely it is to crash max. However, I’m no max expert, and I can’t tell you why.
Modified it to work on Verts on a Mesh. Seems to work just fine
That’s freakin’ awesome matt!
The only thing that I know it was messy was the creation of “stuff” in the background worker thread…
By stuff are you referring to meshes/primitives/nodes/maxWrapper objects? The scripts posted to this thread create stuff. You can even access properties of the maxWrapper class, from which all other max objects extend. This leads me to believe that nothing is “off-limits” to a background worker. But – you are right on the ‘messy’ part – I can’t tell what code will or won’t crash max.
If we could get some input from a developer on how the background worker updates max, we may be able to write stable code around the background worker. For example, if there is a conflict in the viewport that crashes max, then a with redraw off ( ) context might help keep max from crashing. Or, if the processing time of a background function directly affects max’s stabililty, then we could break code into chunks that execute relatively quickly. All of this is assumption however. What’s stopping maxscripters from really utilizing the power of the background worker is a lack of documentation on how it interacts with max.
Here’s some questions I have (if you feel you can answer them, please do):
- When a background worker completes it’s task, how does it let max know it’s finished?
- How does max know when to redraw the viewport after a background worker is finished?
- Where (in memory) is the background worker kept? Does a background worker share memory with max?
- If two background workers attempt to access and modify a scene node at the same time, does a race condition occur?
- If a background worker updates a scene while a non-background function is updating the scene, does a race condition occur?
I’ll be testing a bit more
i’ve really played with the background worker. and i have a conclusion…
before i tell it… what was it wished?
can anyone give me a real profit of the using the background worker in max scripting?
i believe that’s very cool to keep creating boxes when a background worker is stamping spheres… but could you give me any more… practical sample?
so i would like to know what kind of magic you wish from the background worker.
The only reason I would use the background worker is to stop the UI from freezing so the user can see a progressbar and labels etc update. I am currently using the ProgressStart() ProgressUpdate() etc functions because a progressbar in a rollout is pointless if the script runs longer than about 5 – 10 seconds, especially on win7.
System.Windows.Forms.Application.DoEvents method would be useful if you could restrict the message handling to purely redraw messages.
Don’t ask me how this works, but it fixes the freezing UI and allows to use standard progress bars for heavy stuff. I wonder if there are side effects though. Check out that thread, if you haven’t already.
http://forums.cgsociety.org/showthread.php?f=98&t=958314&page=2&pp=15&highlight=background
fn DisableProcessWindowsGhosting =
(
if classof (dotnet.GetType "DisableWindowsGhosting")!=dotNetObject do
(
local source = StringStream ("using System.Runtime.InteropServices;public class DisableWindowsGhosting{[DllImport(\"user32.dll\")]public static extern bool DisableProcessWindowsGhosting();}")
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.GenerateInMemory = on
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source as String)
flush source
close source
if (compilerResults.Errors.Count > 0 ) then
(
local errs = stringstream ""
for i = 0 to (compilerResults.Errors.Count-1) do
(
local err = compilerResults.Errors.Item[i]
format "Error:% Line:% Column:% %
" err.ErrorNumber err.Line err.Column err.ErrorText to:errs
)
format "%
" errs
undefined
)
else
(
compilerResults.CompiledAssembly.CreateInstance "DisableWindowsGhosting"
)
)
It would be cool to apply slow single threaded modifiers, like proboolean, to multiple objects at the same time, making them multi threaded, if that’s possible.