Values passed back to Python from pymxs MAXScript functions that take a by-reference parameter can be translated to Python values using the pymxs.byref()
and pymxs.mxsreference()
functions. In MAXScript, 'by reference' means that a parameter is defined and has an initial value before it is passed to a function (known as an 'in value'), and may be modified by the function operation (known as an 'out value'). By reference parameters are indicated with a '&' character. For more information about this feature, see the "By Reference Parameter Passing" topic in the MAXScript Help. This behavior has no direct parallel in Python.
The pymxs.byref()
function is introduced in 3ds Max 2021, and is the preferred way to work with by-reference parameters, as it provides a more 'Pythonic' way of handling these parameters.
pymxs.byref(PythonValue | MXSValue)
This function marks a value to be used as a by-reference parameter. When you mark a parameter with byref()
, the called pymxs.runtime
function returns a tuple that contains, in order, the result of the function call, and then the updated value of each byref()
parameter. Values passed to byref()
are not modified in place, as they would be in the equivalent MAXScript function call. In other words, parameters marked with byref()
are not updated by the function call, even if they are considered "output parameters". The updated value is contained in the returned tuple.
If a by-reference parameter is only an "output parameter", an actual value does not need to be supplied to byref()
, the value can be passed as None
. If the parameter is an "input parameter" or "input and output parameter", a value does need to be supplied.
The values passed to byref()
can be either Python values or MAXScript values.
For example:
import pymxs
rt = pymxs.runtime
b = rt.box()
# call cloneNodes(), which takes a named by-reference parameter 'newNodes'
# this is only an out parameter, so we can pass it as None
result, myClonedNodes = rt.maxOps.cloneNodes(b, cloneType=rt.Name('reference'), newNodes=pymxs.byref(None))
print('Result: {}, clonedNodes: {}'.format(result, myClonedNodes))
# colorById also takes an out parameter, but it is not named:
res, my_color = rt.maxOps.colorById(2, pymxs.byref(None))
print('my_color: ', my_color)
# when dealing with an in AND out parameter, the value is returned in the tuple
# NOT modified in place, as it is done in MAXScript
theFileName = 'Simple_Stone_Mtl_Marble_bump.jpg'
result, theFilePath = rt.FileResolutionManager.getFullFilePath(pymxs.byref(theFileName), rt.name('bitmap'))
print ('the input parameter was not changed: ', theFileName)
print ('the returned output parameter is set to the full path: ', theFilePath)
The output is:
Result: True, clonedNodes: [<Box<$Box:Box004 @ [0.000000,0.000000,0.000000]>>]
my_color: (color 255 0 0)
the input parameter was not changed: Simple_Stone_Mtl_Marble_bump.jpg
the returned output parameter is set to the full path: C:\Program Files\3ds Max 2021\Maps\adskMtl\Simple_Stone_Mtl_Marble_bump.jpg
pymxs.mxsreference(PythonList | MXSValue)
As of 3ds Max 2021, this function is marked as deprecated, and will produce a warning in the Listener window when called the first time. Use pymxs.byref()
instead.
If the parameter is an "out parameter" (meaning the function updates its value), it will be updated if:
It will not be updated if:
The following MAXScript types are auto-converted to Python types: string, int, int64, intptr, float, double, boolean, objectset, collection (but not bitarray or MXSModifierArray).
For example:
import pymxs
from pymxs import runtime as rt
my_sphere = rt.sphere(radius=10)
my_type = rt.name("reference")
offset1 = rt.Point3(50.0, 50.0, 10.0)
# create a Python list to update with the new nodes:
my_returned_list = []
# pass it using mxsference() so it can be updated:
rt.maxOps.cloneNodes(my_sphere, offset=offset1, cloneType=my_type, newNodes=pymxs.mxsreference(my_returned_list))
print("python array: {}".format(my_returned_list))
# we can also use a MAXScript Array object to pass:
rt.mra = rt.array()
my_result_array = rt.mra
offset2 = rt.Point3(-50.0, -50.0, 10.0)
rt.maxOps.cloneNodes(my_sphere, offset=offset2, cloneType=my_type, newNodes=pymxs.mxsreference(my_result_array))
print("runtime array: {}".format(my_result_array))
The result printed to the Listener is:
python array:
[<Sphere<$Sphere:Sphere005 @ [50.000000,50.000000,10.000000]>>]
runtime array:
#($Sphere:Sphere006 @ [-50.000000,-50.000000,10.000000])