Fundamental Concepts of the MAX SDK
See Also: Must Read Section for All Developers, Advanced Topics.
Overview
This section provides an overview of the fundamental concepts involved in using the SDK. It provides an explanation of many of the basic issues that developers deal with while creating plug-in applications.
The SDK provides tremendous control over almost all aspects of MAX. For this reason, there is a lot of information. However, broken down into smaller parts, things aren't complex. This section will discuss most of the basic issues in simple terms, and has hyperlinks to the sections in the SDK that contain more detailed information.
The Object Oriented Philosophy of MAX
3ds max can be thought of as an operating system for 3D graphics. What a user is doing when running MAX, for the most part, is using plug-ins. Almost all areas of 3ds max are open to developers, and the 3ds max programmers use the same SDK and tools as third-party developers.
Instances of the plug-in classes developer create are called Objects. The way that they can be built independent of one another, yet work together to function as an system, is what makes the 3ds max plug-in architecture so powerful. The biggest impact this object oriented design has on developers is that one can develop plug-ins independently, without having to know or worry about how the other plug-ins are going about their tasks. Developers do have to understand the 3ds max Reference Architecture, however. The Reference Architecture (discussed later in this topic) is the system that allows all the plug-ins to communicate.
Plug-In Types
All of the following areas of 3ds max are implemented as plug-ins: shapes and splines, patch objects, particle systems, modifiers, space warps, lights, cameras, bitmaps, texture maps, materials, image filters and compositors, animation controllers, file import/export utilities, atmospheric effects, renderers, and sound support.
For a list of all the plug-in types that can be developed with the SDK see the topic Plug-In Types Overview.
MAX Plug-Ins and the Tools for Creating Them
This section describes the type of executable used for 3ds max plug-ins and the tools used to create them.
Plug-Ins for 3ds max are developed with Microsoft Visual C++. Plug-ins are implemented as Windows Dynamic Link Libraries (DLLs). A dynamic-link library (DLL) is an executable file that acts as a shared library of functions. Dynamic linking provides a way for a process to call a function that is not part of its executable code. The executable code for the function is located in a DLL, which contains one or more functions that are compiled, linked, and stored separately from the processes that use them.
For step by step information on how to set up a new plug-in project using Visual C++ see the section Creating a New Plug-In Project (this includes information on the AppWizard that's availalble to quickly create projects). For information on debugging plug-ins using the Visual C++ debugger see the topic Debugging. Once your plug-in is working you can optimize it for speed. See the section on Profiling Plug-in Performance for this information.
An additional tool that is helpful in searching for information on specific methods in the C++ source code is \MAXSDK\HELP\SDKLINK.ZIP. This tool launches the SDK online help and jumps directly to the reference information on the selected method. To use this program unzip it and review the installation and use instructions in the README.DOC file.
There is also a file, USERTYPE.DAT, which can be used to enable syntax highlighting of user defined keywords in VC++ 6.0 IDE. This can make browsing and writing code that much more enjoyable. This file lets you get all the 3ds max methods and definitions highlighted in a custom color inside of Visual C++ (like the colored C/C++ keyword you have now).
To put this tool in place, copy the file \MAXSDK\HELP\USERTYPE.DAT into the \DevStudio6\Common\MSDev98\Bin directory. After restarting VC++, you can then change the color of 3ds max keywords by modifying the color under Tools/Options/Format/Colors/User Defined Keywords. It is recommended that you choose a different color than the color assigned to Tools/Options/Format/Colors/Keywords, which are specific to C++. Having two different color patterns aids in your understanding of the code.
The C++ Class Hierarchy
This section describes the hierarchy of C++ classes used to develop plug-ins. C++ classes are used by 3ds max because of their efficiency. When a plug-in is linked directly into the parent program, as it is with Windows DLLs, program flow can proceed directly from 3ds max into the plug-in. The plug-in and 3ds max share memory and can directly access one another's data.
The majority of 3ds max plug-ins are related to modeling, animation and rendering. These plug-ins are generally derived from a series of base classes that provide common characteristics such as the ability to appear in Track View and the ability of the plug-ins to communicate with one another as they are modified and changed.
Three Base Classes of the MAX Class Hierarchy
At the base level of this hierarchy is the animation related class named Animatable. The primary methods of this class relate to appearing in Track View and dealing with memory management. Derived from Animatable are two classes used in the communication between different parts of MAX.
These are ReferenceMaker and ReferenceTarget. These two classes are used to facilitate communication between dependent objects in MAX. This inter-object communication is described later in this topic.
There are other plug-ins that are not derived from these classes. Generally, these are items that don't directly relate to animation. For instance, import / export plug-ins simply get data in and out of MAX. They don't appear in Track View, and function independently of other plug-ins.
For a more complete look at the class hierarchy see the section Plug-In Architecture Overview. For a summary of the main methods of these classes see Overview of the Principal Classes.
How Plug-Ins Interact with MAX
This section discusses the ways 3ds max calls functions provided by the plug-in, and how plug-ins can call functions provided by MAX.
Plug-ins are derived from a base class provided by MAX. For example, if you wanted to develop a geometric object plug-in you might derive your class from class SimpleObject as shown below.
class MyGeomObject : public SimpleObject {
// ...
};
The 3ds max header files include the definition of the SimpleObject class. It is your responsibility as the programmer to provide implementations of some of the required methods of SimpleObject in your plug-in. Said another way, your plug-in implements (provides the code for) certain methods of the base class. 3ds max can then call these methods on your plug-in. For example, SimpleObject has a method BuildMesh(). This method is called by 3ds max when it needs to get a triangle mesh representation of your geometric object. In the code for your implementation of SimpleObject::BuildMesh() you generate the mesh you need. When 3ds max needs this mesh representation it calls your BuildMesh() method.
In other cases, methods are provided by the base class (by MAX). That is, the base class itself provides the implementation. These methods may be called by the plug-in. These methods are inherited from the base class from which the plug-in is derived. For example, a geometric object primitive may be derived from class GeomObject. This geometric primitive can call NotifyDependents(). This is a method of ReferenceTarget that the primitive inherited via the base class hierarchy (GeomObject->Object->BaseObject->ReferenceTarget).
In the SDK documentation, methods that the plug-in implements are labeled 'Implemented by the Plug-in'. Those that 3ds max provides the implementation for are labeled 'Implemented by the System'.
There are other ways that plug-ins can call methods provided by MAX. This is done through a pointer to an Interface Class. An interface class is simply a class without data members and only pure virtual methods. Classes such as these are essentially just a table of function pointers. Interface classes are used quite often in 3ds max programming. The most general interface class is named, logically enough, Interface. A pointer to this class is passed into a great many methods and plug-ins use it to perform common operations provided by MAX. For example, when a modifier plug-in wants to show its user interface in the command panel it calls a method of Interface named AddRollupPage() to take care of it. Or when a plug-in wants to retrieve the current time as specified by the frame slider in MAX, the Interface method GetTime() is called. See the Advanced Topics section Overview of Interface Classes for more details.
How Plug-Ins Interact with Each Other -- The Reference Architecture
In the object oriented paradigm of MAX, where independent plug-in objects work together to facilitate the overall functioning of the system, some form of inter-object communication is required. The Reference Architecture is the mechanism 3ds max uses to handle this communication. Consider the following example to understand why it is needed.
One of the 3ds max animation controller plug-ins is called the Look At Controller. This is a transform controller, meaning it controls the position, rotation and scale of an item in the scene. The Look At controller rotates the item it's assigned to so that it is always pointed towards (or "looking at") another object. For example, say you create a Free Camera and assign the Look At controller as the camera's transform controller. You then pick a Box in the scene as the target of the Look At controller. This will cause the Free Camera to rotate such that its direction of view is pointing right at the Box. Whenever the Box is moved, the controller will readjust itself so that the camera remains pointing towards it. Also, if the Box is deleted from the scene, the camera will recognize this and simply remain stationary.
Now consider that each of these items (the Camera, the Look At Controller, and the Box) are all plug-ins. And each is written without any specific knowledge about the other objects. How then can these objects communicate so they are aware of one another? For example, how does the Camera know that its direction of view changes when the Box moves? Or how does the Look At controller know that the Box has been deleted from the scene? The answers are that these objects communicate via what are called References.
A Reference is a way to associate two items. One item is considered dependent upon the other. In the example above, the Look At Controller is dependent on the Box. And the Camera is dependent on the Look At Controller. That is to say, if the Box changes in some way it needs to let other objects that might depend on it know it has changed. The controller, via the Reference, gets notified that the Box has changed. And the controller needs to do the same for those items which depend on it. When the controller has changed, it lets those items which may depend on it know. Thus the camera gets notified.
An object sets up this record of dependency by creating a Reference. When an object makes a reference, it is called a Reference Maker. The object that is references (that it depends on) is called a Reference Target. In the example above, the Camera is a Reference Maker. It references the Look At Controller. The Look At Controller is the Reference Target of the Camera. The Look At Controller is also a Reference Maker -- it references the Box. The Box is the Reference Target of the controller. The Box, in fact, has its own references, for example, its transform controller.
The inter-dependencies created through References exist all over MAX. In a simple scene, dozens of references exist (many that 3ds max is using internally). In a complex scene, hundreds may exist.
For additional details on how References are used for inter-object communication see the Advanced Topics section References. Also see the sub-topic in that section called Viewing Reference Messages. It discusses a utility plug-in called the Reference Watcher. This plug-in may be used to help understand the reference structure of a choosen item and to monitor the reference messages it sends. This provides a quick, visual way to examine the use of references in MAX.
The Parametric Nature of MAX Objects and the Impact on Plug-Ins
The geometric objects provided by 3ds max are parametric objects. This means they are defined by their user interface parameters, rather than directly by the vertices and faces which make up a mesh surface. For example, a Sphere primitive is defined by its Radius, Segments count, Hemisphere setting, etc. These are what are stored in memory, and saved by 3ds max when the sphere object is written to disk. A triangle mesh representation of the Sphere is required for rendering, but it is computed 'on the fly' from the parameters. Contrast this with the older 3D Studio for DOS. Its objects were not parametric and existed only as the vertices and faces that described the surface. If a user wanted to go back and modify the sphere's segment count she would have to delete and re-create a new sphere object with the setting changed. In 3ds max one can simply change the segment count at any time and the surface is regenerated.
Geometric objects must eventually be converted to triangle meshes for rendering, however. The 3ds max renderer works with mesh objects only, so primitives, booleans, loft objects, particles, patches and NURBS surfaces all are converted to triangle meshes.
In addition to procedural objects, 3ds max provides an extensive collection of Modifiers. Many of these are applied to objects to provide some form of modification, deformation, manipulation or editing of the underlying object. Users can also apply Space Warp plug-ins to alter geometry based on world space positions. The implication of all this for plug-in developers is that they need to understand the conversion process from parametric object to modified triangle mesh.
The Geometry Pipeline System of 3ds max is used to handle this processing and conversion. The geometry pipeline is the system used to allow parametric objects to be operated on by modifiers, with the eventual generation of a triangle mesh suitable for rendering and display in the viewports. For the full details see the Advanced Topic section Geometry Pipeline System. For information regarding how procedural objects and modifiers work together to facilitate the modification see the section Object Modification.
Plug-in developers may find helpful to study the code for the Collapse Utility. This utility lets a user collapse the Stack of one or more selected objects into an Editable Mesh and, optionally, perform a Boolean operation on them at the same time. See the code in \MAXSDK\SAMPLES\UTILITIES\COLLAPSE.CPP.
The Scene -- Nodes
The 3ds max Scene, that is the objects, lights, cameras, space warps, particles, etc., are each associated with a Node. There is a one to one correspondence between each item in the scene and a node. A node is simply the item that manages the information needed to allow its associated object to exist in the scene. These are things such as the transform controller, the material used, data about parent-child hierarchies, and grouping information.
The transform controller of the node controls the position, rotation and scale of the object in the scene. The material assigned to the node is used by the renderer to give the object its surface characteristics such as color, texture, bumpiness. The hierarchy information is used to handle the linked / unlined state of objects for forward and inverse kinematics.
For more detailed information see the Advanced Topics section Nodes, Working With Materials and Textures, and Working with Controllers.
Time and Intervals
Developers dealing with animation need to specify certain points in time, and also certain periods of time. This section discusses each of these.
The basic unit of time in 3ds max and the SDK is equal to 1/4800th of a second. This length of time is a data type called a TimeValue. TimeValues are used throughout the class methods in the SDK when a specific instant in time needs to be specified. For example, if your plug-in needed to know the current system time (position of the 3ds max frame slider) it would call the methods Inteface::GetTime(). This methods returns a TimeValue that indicates the current time (in 1/4800ths of a second).
In MAX, intervals are commonly used to define the time period in which an object is unchanging. To be as fast as possible 3ds max avoids re-computing objects whenever it can. If an animated plug-in can specify how long a period it is unchanged 3ds max can know it doesn't have to re-evaluate the object over that time. For example, 3ds max needs to know over what length of time the Modification done by a modifier is valid for. Consider a Bend modifier where the user has animated the bend angle over time (say un-bent from frame 0 to frame 50, and then bent 45 degrees at frame 100.) So that 3ds max doesn't need to re-evaluate the modifier every frame (after all, its not bending from frame 0 to 50) the modifier provides 3ds max with a Validity Interval that tells it how long the deformation is valid for. This validity interval is relative to a certain time. For instance, say 3ds max asks the modifier how long the modification is valid for at frame 0. The modification is valid from frame 0 to frame 50. If 3ds max asked how long the modification was valid for after frame 50, say at frame 75, then the answer is only for a single instant of time. That's because the modifier is animated from frame 50 to 100. At each frame it is different (bent more towards the final 45 degrees). Therefore the validity interval is only a single point in time (or TimeValue).
Intervals of time are specified in the SDK using an Interval object. This object has a start and end time and various methods to work with the interval. There are numerous methods that a plug-in implements to specify validity intervals in MAX. The modifier example above would do so in the method Modifier::LocalValidity(). For more information on Time in the SDK see the Advanced Topics section Time. More information on intervals can be found in the topic Intervals.
Animation Controllers
All animation in 3ds max is managed by a plug-in type called an animation controller (or controller for short). Some controllers are keyframe based. This means the user sets the value of the controller at important (key) points in time and the controller provides all the values at other times by interpolating between the key values. Others are procedural, meaning the value is computed at a certain time based on a pre-programmed effect (for instance, the Noise controller uses a fractal function to compute the value).
There are six basic controller types based on the data type they control: floating point values (float), three float values (Point3), Position (Matrix3), Rotation (Quat), Scale (ScaleValue), and Transform (Matrix3). Using the SDK developers can create new controller types, or work with the properties of existing types. See the topic Working With Controllers for more information.
Many plug-ins use animated parameters, for instance the animated radius parameter of a procedural Sphere. 3ds max provides tools for developers wanting to use animated parameters that make them very easy to manage. Two of these are Parameter Blocks and Parameter Maps. Parameter Blocks provide a mechanism for storing values for a plug-ins parameters and managing the controller which handle the interpolation or generation of values. Parameter Maps are used to associated a control in the user interface of the plug-in with an animated value. See the Advanced Topics sections Parameter Blocks and Maps in Release 3 and Later, Parameter Maps and Parameter Blocks for details.
User Interface Issues
The user interacts with 3ds max mainly via the command panel rollups/dialogs, the mouse (or tablet), the keyboard, and the 3D viewports. The SDK provides developers with the ability to control each of these areas of user interaction.
Plug-Ins generally provide their user interface controls in one of three ways: In one or more of the Command Panel branches, in rollups which appear in the Materials Editor, Environment, or Render dialogs, or as modal or modeless floating dialogs. Many of the buttons, spinners, edit fields, etc. that appear in these dialogs/rollups are from the set of the 3ds max provided Custom Controls. These are used to make the UI of plug-ins consistent in appearance from one to another. For more information on these controls and how to work with them see Custom User Interface Controls.
In addition to the custom controls, the mouse is used to interact with MAX. The way this type of interaction is handled in the SDK is through what are called Command Modes. For more information see the section Command Modes and Mouse Procs.
Plug-ins can also make use of the keyboard to speed user interaction with their plug-in. This is done by registering what are called Keyboard Accelerators. These allow the user to execute some of their plug-in's functions by assigning them to keystrokes. For information on how this is done see the Advanced Topics section Keyboard Accelerators and Dialog Messages.
Finally, plug-ins often need to draw in the 3D viewports to show themselves or their 'gizmos'. This is accomplished using the methods provided by the interactive renderer class GraphicsWindow. For information on the way to work with this renderer see Interactive Renderer: Graphics Window.
Functions Every Plug-In Must Provide
This section describes a set of five functions required by every plug-in DLL. It also outlines an object developers need to provide for each plug-in class.
Every plug-in developer needs to provide the code for the DllMain function and four Lib functions. Each is described briefly below:
Function Called to Handle Initialization
DllMain() -- This function is the hook used by Windows to initialize the DLL. 3ds max plug-ins use it to initialize the common controls library and MAX's custom controls.
Functions Used by MAX to Inventory and Categorize the Plug-ins in a DLL
These four functions describe the number and properties of the plug-ins provided by the DLL.
LibNumberClasses() --This function returns the number of plug-in classes contained in the DLL.
LibVersion() -- This is a function that allows the system to deal with obsolete versions of plug-in DLLs.
LibDescription() --This function returns a text string to present to the user if the DLL is unavailable.
LibClassDesc() -- This function returns a pointer to an object called a Class Descriptor for each plug-in class in the DLL. This Class Descriptor object describes the properties of each plug-in class and a way to allocate an instance of the class in memory. The next section describes these Class Descriptors.
Creating and Classifying Instances of Plug-In Classes
The Class Descriptor described above is an object derived from class ClassDesc. It has several important purposes. The two main ones relate to classifying the type of object the plug-in is, and allocating the memory for instances of the plug-in objects. The full details of Class Descriptors are described elsewhere but three important methods are described below.
Plug-ins of all types create 3ds max system objects. These are not the 3D objects that appear in a scene, but objects in the C++ sense. There are two IDs associated with each plug-in system object. These are the Super Class ID and the Class ID. The Super Class ID specifies what super-class of 3ds max the plug-in class is a sub-class of. The Class ID differentiates between the various plug-ins for a super-class. For example, a Sphere object has a Class ID, unique to it. This is simply an ID generated using a program provided by the SDK that uniquely identities the plug-in class (it is in \MAXSDK\HELP\GENCID.EXE -- Click Here to Try It). Its Super Class ID, which is shared by all geometric primitives, is GEOMOBJECT_CLASS_ID. The Super Class IDs are defined by MAX, and each plug-in falls into one of these predefined categories. For instance, all Texture Map plug-ins share the same Super Class ID of TEXMAP_CLASS_ID. Each individual texture map plug-in has its own unique Class ID however. Thus, the Super Class ID defines which kind of object it is, the Class ID uniquely identifies a specific plug-in class.
The Class Descriptor method ClassDesc::SuperClassID() returns the Super Class ID. The method ClassDesc::ClassID() returns the Class ID.
Another function of the class descriptor is to allocate instances of the plug-in class. For example, when a 3ds max file is loaded which contains a procedural Sphere, 3ds max needs a way to create an instance of the Sphere plug-in. It calls a method of the Sphere's class descriptor (ClassDesc::Create()). See the next section for more on this method.
The functions outlined above are described in greater detail in the Advanced Topics section DLL/Lib Functions and Class Descriptors.
The Memory Allocation and Deallocation of Plug-In Classes
As discussed above, instances of plug-in classes are created by calling Class Descriptor's Create method (ClassDesc::Create()). This applies to all plug-in classes in the SDK.
The way this memory is freed varies based on the class the plug-in is derived from. Usually it is done by calling a method named DeleteThis() on the plug-in. In the classes that are part of the Animatable class hierarchy this is a method of Animatable. So for these plug-ins, the memory is allocated by ClassDesc::Create() and deallocated by Animatable::DeleteThis().
For other plug-ins that are not derived from Animatable, the plug-in class itself may provide a DeleteThis() method. For example, the Bitmap class is not derived from Animatable. It however provides its own DeleteThis() method to deallocate the memory created by ClassDesc::Create().
In other cases, memory is freed automatically by 3ds max itself when it is done with the plug-in. For example, the ImageFilter class used to create image processing effects for use in Video Post has its memory freed by 3ds max itself (by using the delete operator).
See the Advanced Topics section Memory Allocation for more details.
Summary
This topic has provided at look at some of the fundamental issues developer need to understand to create 3ds max plug-ins. The Advanced Topics sections in the SDK provide detailed information about many different aspects of the API. To review the relevant parts of the SDK that relate to a certain plug-in type see the section Must Read Sections by Plug-In Type.