Home

June 16, 2015, 4 min read

mxs_types - MAXScript Datatypes for Development

For an integrated scripting language, MAXScript really is not so bad. It is very powerful for automations within the software and can be used for more generalized tasks on top of that, like reading/writing/moving files,  if you wish to do so. I have seen people implement fully-fledged 2D fluid solvers plus visualization in it (not saying that it is a great idea, but hey, you can!)

However what it seriously lacks are some common data structures that are used in other languages for day-to-day development tasks. The most prominent example for this is the lacking of a dictionary datatype in MAXScript; we can not easily define arbitrary mappings from keys to values. While the underlying C++ SDK has a Hashtable for this, it is (for some mysterious important reason) not exposed to the MAXScript engine. Dictionaries are just so very versatile, easy to use and provide great lookup performance. Coupled with lists, you can define almost any complex data structure; Just check out the JSON file format, it is simply a combination of lists and dictionaries.

Python has all this, but the current state of the native Python support inside 3ds Max 2015+ is rather disappointing and the Blur guys seem to have abandoned their Py3dsMax project (at least the public version), so that is also not an option. While I hope it will get better in the future, I often find myself knee-deep in MAXScript code until then.

Since there is no builtin solution, people have started writing their own implementations, going from very simple structs that map this to that, to more generalized solutions with more or less better performance. Some examples: The first example uses an underlying .NET hashtable to implement the dictionary. This provides proper read performance, since we do not have to iterate a list and can use a fast hashvalue-lookup. However, it also limits us to use only strings for both keys and values, since this is the only thing the .NET hashtable understands. The second implementation is more flexible, but sacrifices a bit of speed. We can only use strings as keys, but anything as a value, which is great. The lookup performance is increased by sorting the keys on every addition/update (which in turn decreases the write performance a bit) so we can do a quick binary search on every lookup. For me, flexibility is more important with these structures than performance. The number of elements that would need to be stored in the dictionary would (in most use cases that I encounter) not exceed a few hundred or a few thousand.

However for situations where performance really is an issue, it would be nice to have an alternative that follows the same API as a more flexible, slow implementation. The dictionary is not the only thing lacking; some times it would be useful to be able to compare lists against each other, see how they differ, which elements are unique and be able to extract that difference or combine them. A set is what is used for this in other languages.

That is why I wrote some custom datatypes in MAXScript that are similar to use like Python's dict (multiple flavors) and set. Depending on which you choose they are either more flexible and slow (e.g. mxs_dict: Map anything to anything) or more rigid and fast (e.g. mxs_hashdict: String only, but a true hashtable). I tried to make their API's consistent and support them with descriptions and examples for ease of use. They are covered with automated tests to ensure their basic functionality is solid (though I did not test all edge cases). A small performance benchmark between the two dictionary implementations shows no suprises. The mxs_dict will get exponentially slower with increasing number of items, though this will probably only be an issue around the thousands and up.

The mxs_hashdict on the other hand does not care about item count and provides fast lookup at all times. The mxs_set uses a standalone module named mxs_eq to be able to check (nested) composite structures like arrays, dicts or structs for equality, which MAXScript can not do with its builtin tools. Order matters, like in Python. This way the following expression will return true, as you would expect:

a = #(1, 2, #("foo", undefined))
b = #(1, 2, #("foo", undefined))

-- Builtin equality check can not compare arrays:
a == b
>>> false

-- mxs_eq can compare arbitrary nested arrays and other composite structures:
mxs_eq.isequal a b
>>> true

I am sharing the scripts open source. If you have any feedback, questions, found a bug or want to contribute, please go ahead. Please have a look at the repository for the source code, examples and API documentation: https://bitbucket.org/cb109/mxs_types Cheers