Note: The interface in this tutorial applies to MCG 2017. The interface in MCG 2018 has been revised to a new node naming scheme.
In this post, I’ll be covering a little experiment I’ve been working on to create deformers and their respective gizmos in MCG 2017. As the saying goes, “the proof of the pudding is in the eating”, so I put these deformers to the test by stretching, bending, rippling, tapering, and twisting a few primitives into the flower you see below. ? ?
The native deformers in 3ds Max provide you with a gizmo in the modifier stack to help you manipulate and preview the deformer’s frame of reference. By manipulating this gizmo, you can provide further refinements to the deformation. Gizmos can be keyframed and animated, however they do not behave as regular objects in the scene. For example, they can’t be constrained to a path, nor can they be rendered as splines.
Note: right-click on the image and open it in a new tab to see it in a higher resolution.
With these limitations in mind, one of the motivations behind this little experiment was to open the door to different gizmo workflows by defining them as splines in the scene. Under this light, MCG gizmos would act similarly to world-space deformers, while still retaining the behavior of a spline in the scene. This approach makes it possible to constrain gizmos, render them, and even use them to drive multiple deformers.
The result of this project is the seven deformers you see below. You can download and extract them from the .zip file at the bottom of this post. I hope that as I unravel the details of my approach, you will be inspired to create a new family of gizmo-based MCG tools.
From left to right: MCGRipple, MCGWave, MCGNoise, MCGTwist, MCGTaper, MCGBend, MCGStretch.
My first step was to make sure I could easily compare my MCG deformers’ results with the outputs of the native deformers in 3ds Max. By comparing my results with an established baseline, I could validate that I was on the right track. To do this, I decided to guide the implementation of my MCG deformers using the sample code in the 3ds Max SDK.
If you want to browse the 3ds Max SDK for yourself, you can install it from your Autodesk installer’s extraction path (not to be confused with your installation path under Program Files). For reference, mine was under C:\Autodesk\Autodesk_3ds_Max_2017\x64\Tools\MAXSDK\SDK_3dsMax2017.msi. Once you have the SDK installed, you can access the sample C++ source code for the Bend modifier (and other modifiers) under C:\Program Files\Autodesk\3ds Max 2017 SDK\maxsdk\samples\modifiers\bend.cpp.
The Map function of each C++ deformer contains its core deformation logic. In the code above, the “BendDeformer::Map” function is responsible for bending a single point. Note that for the sake of time and simplicity, the MCG deformers I created do not include the “doRegion” logic, and therefore omit the “Limit Effect” options in their respective rollouts.
After a few attempts at implementing and re-implementing several deformers, I began to converge towards a common construction pattern. This pattern, in the context of the Bend modifier, looks like this:
1. Create the Modifier
MCGBend.maxtool ? BendMesh.maxcompound ? BendDeformer.maxcompound
2. Create the Gizmo
MCGBendGizmo.maxtool ? BendSpline.maxcompound ? Custom MaxScript UI
The MCGBend.maxtool file generates the MCG modifier inside 3ds Max. This is where I specified my parameter limits, defaults, and the optional gizmo input node.
BendMesh.maxcompound
BendMesh.maxcompound applies a bend deformation to a whole mesh. The advantage of encapsulating this deformation as a compound is to enable its reuse in other graphs.
BendDeformer.maxcompound
BendDeformer.maxcompound contains the core deformation logic for a given point. If you’re curious about the theory behind deformers, I strongly recommend the following lecture notes by Prof. Torsten Möller from Simon Fraser University. The logic you see below loosely translates to the code found in the bend.cpp file’s BendDeformer::Map function.
The MCGBendGizmo.maxtool file generates the MCG spline you can create under Create > Shapes > Max Creation Graph > MCGBendGizmo. Each gizmo has two Vector3 inputs to define the minimum and maximum corners of the gizmo’s bounding box. These Vector3 values are used as inputs to the SplineBoxPrimitive compound, which generates a subdivided spline box. This spline box is subsequently deformed with the BendSpline compound to produce a preview of the deformation.
An important note about the Shape: Matrix node. The “Shape: Matrix” node must be connected to the graph in some way (we’re using IgnoreSecond to include it, but to omit its output) since it provides us with an auto-generated reference to the current node within the MaxScript plugin definition. We’ll see why this is important in a bit.
Much like the BendMesh compound, the BendSpline compound applies the BendDeformer to all the knots on a given spline.
To streamline the usage of the MCG gizmo in the scene, I cooked up a custom MaxScript UI to add two buttons to the auto-generated rollout, namely: “Update Box from Object” and “Apply to Object”. This way, once a MCG gizmo is created in the scene, you can press the “Apply to Object” button to apply the gizmo’s associated modifier onto an object. Likewise, you can simply use the “Update Box from Object” button to update the gizmo’s bounding box based on the picked object (without applying the modifier).
When an object is picked with the “Apply to Object” button, three things happen: 1) the gizmo updates its bounding box based on the picked object; 2) the MCGBend modifier is applied to the object; 3) the gizmo’s parameters are wired to the modifier’s parameters in a one-way direction.
An important note about gizmo parameter wiring: based on my experiments, creating a two-way parameter wiring causes some instability in 3ds Max when objects are cloned. To avoid this instability, I restricted the parameter wiring to a one-way direction (from the gizmo to the modifier). A side-effect of this one-way connection is that the object cloning process breaks all the original wired connections to the gizmo, meaning that the modifier must be re-applied when one of the wired objects is cloned. As such, I found it helpful to create all my primitives before applying the gizmo.
With the gizmo applied, it now drives the modifier on the picked object. You can use the same workflow to drive the bend deformation of several objects.
Now that we have an idea of how the gizmo behaves, we can take a closer look at how to define its custom MaxScript UI.
Open the MaxScript editor, define an empty rollout, then use the special tags: > and > to let MCG automatically populate these regions with the parameters and event handlers defined in the graph. With these tags in place, you can add your own UI elements below the > tag. Likewise, you can add your own event handlers below the > tag. By using these tags, you no longer have to re-synchronize your custom UI every time you change your graph’s parameters from within the MCG editor.
Now, if we take a little step back - remember that note about the Shape: Matrix operator in MCGBendGizmo.maxtool? Connecting Shape: Matrix into your graph provides the rollout with a valid reference to an owningNode variable. This variable represents the node associated to the gizmo, which we need to use in the call to paramWire.connect. In the absence of Shape: Matrix, the owningNode variable would be undefined, preventing us from wiring its parameters.
I would recommend saving this rollout as a standalone .ms file next to the .maxtool file you want to use it on. This way, you can quickly iterate on its contents. When you’re ready, copy the whole rollout code and paste it into the .maxtool’s Custom UI tab under Max Creation Graph Editor > Edit > Graph Properties > Custom UI Tab. This embeds the custom UI into the .maxtool. The final step is to save and evaluate the .maxtool to see how it behaves in Max. Any errors encountered along the way should be displayed in the MaxScript Listener (F11).
The MCGNoise deformer was one of the most challenging deformers to implement, mainly due to the complexity of the fractal noise code in the 3ds Max SDK. Keep in mind that the output of this deformer will vary compared to the native version because of the different random number generation process inside MCG. What’s more, a portion of the native code relies on the promotion of a single-precision float to a double-precision float - a conversion which can't be performed in MCG due to the absence of the double-precision float type. The consequence of this detail is that for certain seed values, the MCG noise deformer may produce flat regions and/or sharp discontinuities. I found that I could mitigate this issue by exploring different seed values.
Download: MCG_Deformers_and_Gizmos.zip
Instructions: Extract the .zip file anywhere on your filesystem. Copy the MCG_Deformers_and_Gizmos folder into your Max Creation Graph 2017 user directory: C:\Users\
May we collect and use your data?
Learn more about the Third Party Services we use and our Privacy Statement.May we collect and use your data to tailor your experience?
Explore the benefits of a customized experience by managing your privacy settings for this site or visit our Privacy Statement to learn more about your options.