Original MaxScript Tutorial Source Code
Goals:
- open a file selection dialog in 3ds Max
- use file io in Python
- use pymxs.attime, pymxs.runtime.selection, pymxs.runtime.animationRange
- open a text file in the 3ds Max text editor
Non Goal:
- explaining how to connect a Python function to a menu item (this is done in other samples like removeallmaterials)
The following example will create a macroScript that will output the speed of the current object selection on each frame and its average speed to a text file.
A difference with the MAXScript version of the sample is that we use Python file io instead of pymxs file io in the Python sample.
From the 3ds Max listener window we can do:
import speedsheet
speedsheet.startup()If we install this sample as a pip package it will be automatically started during the startup of 3ds Max (because it defines a startup entry point for 3ds Max).
The speedsheet function in speedsheet/__init__.py fully implements this sample.
It first opens a file selection dialog, with a caption and a filter for
file types, using rt.getSaveFileName:
output_name = rt.getSaveFileName(
caption="SpeedSheet File",
types="SpeedSheet (*.ssh)|*.ssh|All Files (*.*)|*.*|")It then opens the selected file for writing using the Python open function.
The file is opened in a with block: this guarantees that no matter what
happens in the block, the file will be closed before exiting the block. This
simplifies the code and makes it more robust at the same time.
with open(output_name, "w+") as output_file:Next, the code produces a list of objects at the first frame
of the animation. Everything inside the with pymxs.attime(sometime) block will
happen at sometime in the time line (this is the same as the at time
construction in MAXScript):
with pymxs.attime(rt.animationRange.start):
objdump = ", ".join(map(lambda x: x.name, list(rt.selection)))
output_file.write(
f"Object(s): {objdump}\n")The map(lambda x: x.name, list(rt.selection)) construct converts the rt.selection
(the set of currently selected objects in 3ds Max) in a list of strings (the name
of these objects).
The ", ".join() call concatenates these strings separated by ", " to produce
a nice list of object names.
The code then iterates the timeline:
for t in range(int(rt.animationRange.start), int(rt.animationRange.end)):It then computes the position of the selection center at t and t-1:
with pymxs.attime(t):
current_pos = rt.selection.center
with pymxs.attime(t-1):
last_pos = rt.selection.centerFrom this it computes the speed at that time and writes it to the file:
frame_speed = rt.distance(current_pos, last_pos) * rt.FrameRate
average_speed += frame_speed
output_file.write(f"Frame {t}: {frame_speed}\n")When the loop terminates, the average speed of the animation is also logged to the file:
average_speed /= float(rt.animationRange.end - rt.animationRange.start)
output_file.write(f"Average Speed: {average_speed}\n")After the end of the with open( block the file is closed automcatically.
We open it the 3ds Max editor:
rt.edit(output_name)