Archive for September, 2006

Visual Breakpoints for the MAXScript Debugger

Monday, September 18th, 2006

I’ve just finished an UltraEdit macro that uses the bookmarks within UE as a form of visual breakpoints for the MXSDebugger. Just load the attached .MAC file in to UE’s macro list to make use of it.

You can download the file here.

When you run the macro the following steps happen:

  • Your current file is saved
  • At any point in the file where a bookmark is found a “break();” statement is added to the beginning of that line
  • The file is saved off as a new temporary .ms file (c:\MXSTemp.ms).
  • The new file is executed in Max via the MXSCOM bridge
  • The temporary file is closed
  • Your original file is reloaded
  • The editing cursor is positioned at the start of the line you were on when you executed the script.

Current Limitations

  • You must have a user tool configured to run the MXSCOM bridge. This tool must be named “Run MAXScript”.
  • If you’re trying this on a newly created file it will be saved off to C:\Program Files\IDM Computer Solutions\UltraEdit-32\Edit#, where “Edit#” is the name of the tab in UltraEdit. Either save your new file off before you run the macro or remember to do so afterwards.
  • I wanted the macro to either only use, or ignore, named bookmarks so you could still have regular bookmarks in your file. Sadly, the macro language in UE doesn’t support querying bookmarks for their names.
  • Sometimes you will come back to your original file after you run the macro and your bookmarks will be gone. UltraEdit stores per file bookmark information in its main application INI file. This file is not updated when you save a document, only when UE is closed. This leads to the unfortunate problem of a new file being closed and opened without UltraEdit being closed and the bookmark information being lost. It doesn’t happen every time, so UE must be temporarily storing that data in memory somewhere.

When Is Up Not Up?

Thursday, September 7th, 2006

By design any nodes placed into the Max scene are oriented so their local z-axis is perpendicular to the construction plane of the viewport in which they are placed. In order to have objects with their +z-axis aligned with the world coordinate system +z-axis you have to place the object in either the perspective or the top viewport. This recently became an issue at Volition, as we wanted to use a scripted helper object to specify snap-points for objects in game. The scripted helper’s axis orientation is directly relevant to how objects are snapped to it. Having the helper placed at different rotations based on something as arbitrary as a viewport selection was counterintuitive. Having the artists constantly reorienting their helper nodes after creation was becoming time consuming.

I figured it would be an easy thing to fix. All I needed to do was to set the rotation of the new node to quat (0 0 0 1) as it was being created…

tool create
(
on mousePoint clickno do (
if (clickno == 1) then (
delegate.shape = “sh-snappoint”
nodeTM.pos = gridPoint
nodeTM.rotation = (quat 0 0 0 1)
) else (
#stop
)
)
)

This didn’t change the behavior at all. The nodes were still coming in oriented relative to the viewport in which they were placed. Confused, I put a “print (nodeTM.rotation as string)” line after the nodeTM.rotation = … line. The script started telling me that each placed node had a rotation of (quat 0 0 0 1), which was good. Each node still ended up in viewport relative orientations, though. querying their .rotation property after they were in the scene yielded rotations other than the default. Somehow, somewhere, Max was overriding my desired rotation before the node was attached to the scene.

After some forum posts with Autodesk it turns out that you have to use getCPTM(), which returns the construction plane to world transform matrix, set the view transformation matrix to that, and then set nodeTM to be equal to the transform matrix of the mouse position on the active grid in world coordinates multiplied by the inverse of the newly set view transformation matrix. Then a placed node will always be oriented to the world.

You might need to read that through a few times. I know I did!

The correct code is:

tool create
(
on mousePoint clickno do (
if (clickno == 1) then (
delegate.shape = “sh-snappoint”
viewTM = getCPTM()
nodeTM = (transMatrix worldPoint) * (inverse viewTM)
) else (
#stop
)
)
)