How do the rest of you stay organized with Maxscript?

I’ve got a giant tool I’ve been putting together over the course of the last six months. It’s a floating tabbed rollout. It covers everything from loading/unloading skeletons (and associated skin data), metadata tagging, animation functions, and automated exporting… there’s a hell of a lot going on in here.

In an attempt to keep things segregated, each of the tabs is written up as its own rollout in its own file. Each of those files are included into the master rollout. The downside to that is typos in the included files causes all of Maxscript to lose its file I/O ability if I try to evaluate my scripts. I actually have to restart Max to get it back, and that’s a huge pain in the ass.

I’d rather find a way to break down logical chunks of operations into smaller, more easily maintained files, and I’m having a little difficulty coming up with a method for doing that. The best that I can come up with is maybe setting up a series of self-contained structs. I figured I’d poll you guys before heading down that road, though. What are some of your solutions for avoiding huge, barely manageable maxscript files?

Not that I’m doing it right, but I’ve taken inspiration from python package structure to organize my maxscript content.

I’ve got a script which gets called from Max upon startup (You need to point Max to this via installer or by manually editing configs). This file is always executed upon every launch (think “sitecustomize.py”). In the startup script, I define a few global variables which define relative directories from which I know the entire install base for everything contained therein. I personally store:

[LIST=3]
[li]The location of the currently running startup script so I know where the user installed my external source files[/li][li]A path to the root of my tools based on parsing the result of #1[/li][li]A path to the actual folder which is the root of all Max scripts (since the tools folder doesn’t just contain Max only content)[/li][/LIST]

For example:


-- Packages paths based on introspection of "RobotArtTools"
ROBOT_STARTUP_ROOT = getFilenamePath(getThisScriptFilename())
ROBOT_ARTTOOLS_ROOT = substring ROBOT_STARTUP_ROOT 1 ((findString ROBOT_STARTUP_ROOT "RobotArtTools")+"RobotArtTools".count)
ROBOT_SCRIPTS_ROOT = pathConfig.appendPath ROBOT_ARTTOOLS_ROOT "MAX/Scripts/"

Once I know where each of these directories live, I know the entire layout of my custom library, and can get to anywhere by starting from one of the variables above. Also in the startup script is a function to load any package (folder of max scripts) I tell it to assuming package modules end with a valid .ms or .mcr extension.


fn load_package root =
	--
	-- Discovers all ".ms" files recursively below the provided root directoy
	-- and loads them into memory for immediate use.
	--
	-- :param root: Path to the package directory
	-- :type root: String
	--
(
 -- TODO: Type load logic here :)
)

The last part of the startup script uses an array of hard coded packages names which I always load at startup. This could be configured via .ini or likewise and located via instrospection if so desired, but I haven’t done that as of yet. I call the “load_package” function and that tears through all the folders, locates the script files, and pulls them into memory using filein command.


packages = #()
append packages (pathConfig.appendPath ROBOT_SCRIPTS_ROOT "robotools/")
append packages (pathConfig.appendPath ROBOT_SCRIPTS_ROOT "external/")
append packages (pathConfig.appendPath ROBOT_SCRIPTS_ROOT "legacy/")

for package in packages do
(
	load_package package
)

In order for this to work, all modules in a package (maxscript in a folder) are unqiuely named as to not name clash. I name them after the type of functions they hold, IE: “roboanim.ms” holds functions related to animation tasks. Each function (def) in my module (maxscript file) have names which are prefixed to match the name I used for the package (folder that holds the scripts), IE: def roboanim_bake() is found in module (script) roboanim.ms in package (folder) “robotools”.

At the top of each modules (maxscript) I have a few calls to other modules which must be sourced into memory so that the current module functions run properly (think python includes). For example, module roboanim.ms has a first line that reads:


filein (ROBOT_SCRIPTS_ROOT + "/robotools/lib/robonode.ms")

because there are a bunch of calls to functions related to node manipulation.

I haven’t done much with UI yet, but I can tell you that it’s fantastic to separate code used to generate UI from code driven by UI callbacks. Then when your UI crashes it’s a matter of rebuilding the UI and not having to restart max because the whole function is borked.

Hope this helps. I’d like to hear other input as well as I’ve arrived here coming from a Maya python background.

-csa

I’ve tried getting non-plugin type scripts to run on launch, but I haven’t had much success. I’d love to be able to have the character artists do a get on the repo that holds all my scripts and then have Max auto-update all the UI and functionality changes by evaluating those scripts on launch. What am I doing wrong? What configs are you modifying to make that work?

What csa3d does is pretty much how I’ve deployed for 3dsMax. You shouldn’t encounter any issues and your tools should be the “latest” assuming users get/sync to latest on the repo/depot. They will have to close and re-open the tool, but not 3dsMax.

One tip is to make sure your macros simply filein a seperate .ms with the actual tool code, don’t put your code in the macro declaration itself. Macros are evaluated on 3dsMax load along with the UI. Doing this ensures that a user does not have to restart 3dsMax after getting/syncing from a repo/depot (you can re-evaluate the macro, but again this is not what you want a user to be doing every time you modify a script).

Here’s an example of a macro that does this:

macroScript SuperTool
  category:"Custom Tools"
  internalCategory:"Custom Tools"
  buttonText:"MySuperTool"
(
    fileIn ( TOOLS_ROOT_DIR + "	ools\superTool.ms" )
)

3dsMax doesn’t have anything that resembles Maya’s module files, so we also use a setup script for first time installation to get 3dsMax to point to our directory structure and plugins. We found it was less overhead than building a deploy tool, and the advantage is that we can keep the 3dsMax installation clean. If a user encounters problems in 3dsMax down the road we wipe his appdata folder (“C:\Users<user_name>\AppData\Local\Autodesk\3dsMax<3dsmax_version>\ENU”), launch 3dsMax and run the setup again. Here are the main steps of the setup script:

[ul]
[li]Modify the user’s 3dsMax.ini and plugin.ini to add local folder entries.[/li][li]Setup scene defaults (units, and 3dsMax properties)[/li][li]Define project location (write to disk globals, this is useful for out-sourcers)[/li][li]Anything else you want to have done permanently to a user’s 3dsMax installation (change loading screen for instance).[/li][li]Once everything is done, start a new instance of 3dsMax and close the current instance of 3dsMax (essentially poor man’s restart).[/li][/ul]

Hope this helps.

Got a setup python script that installs the startup file into the appdata and that loads in all the ms files into the toolbar.

I use a system I learned from a web page for a Auto desk university Class by Paul Neale on Advanced Tool organization for Max script.
Autodesk has since eaten the URL, sadly. I’ve tried to google it many times.

The basic idea is that you simply store your scripts organized nested directories.
At the base level you have an “ubertool” max script and one or more “methods” max scripts.
Next to that is a “tools” folder : contains sub folders for independent rollout tools that can call on functions in the “methods” script.

uber tool is a split rollout panel where you can navigate and load tools from folders using a dot net tree view.
navigating to the tool in the tree view loads it in a sub panel on the right

Using this system I can keep all my tools in one parent folder, wherever I want,
ignoring arbitrary Max folder system requirements for macroscripts or user scripts,
and load the “ubertool” single line file in script.
Individual tools can be broken without breaking the uber tool (until you try to load a broken tool)

I package all of them for install with a NSIS script per this article

pm me if you want, I can zip and send the source max script.