In a 3ds Max scene the objects that a user can create and manipulate in the viewports are called scene objects. Scene objects derive from the Object class. Examples of scene objects are geometric objects (GeomObject), lights (LightObject), cameras (CameraObject), particles, and world space modifiers (not to be confused with regular modifiers). Scene objects can have parent-child relationships to form a hierarchy and let users create compound objects. Transforms applied to the parent are also transmitted to child objects. You can refer to Animation > Hierarchies and Kinematics > Hierarchies in the 3ds Max online reference to review the parent-child relationship among the scene objects from the user's perspective. To understand the relationship between scene graph nodes in modifier stack, read the topic Pipeline Overview .
3ds Max stores the information about scene objects as well as their relationship to each other in a data structure called the Scene Graph. Each node of this graph is an object of the class INode that corresponds to one scene object. Each INode object has information about its corresponding scene object including (but not limited to) a pointer to the object, the number of child nodes, the address of the parent and the child nodes, the position, rotation and scale (PRS) transform controllers for that object, and the material applied to the object. Note that the nodes in the scene graph are not the real scene objects but rather have a pointer to their corresponding scene objects. The scene objects themselves do not have any information about their PRS controllers, their materials, their relation to the other objects, etc.
The edges of the scene graph are directed edges that are called links in 3ds Max. There is a link from node A to node B if the state of node A (e.g. position, rotation, scale, material, etc) depends on that of node B. Each object can be followed or linked by many objects but can link to only one object. No loop is allowed in the links of the nodes. These together make the scene graph a tree data structure. The root of this tree is a virtual node that is not linked to any other node (the only exception). The nodes corresponding to the independent scene objects that do not have a parent object in the scene are the first level nodes in this tree. Even if their object is instantiated from their classes, the scene objects do not get displayed in the viewport unless they have a corresponding node in the scene graph.
The INode class is an important class in the 3ds Max SDK and has a large number of member functions. In this lesson we review a small set of INode's member functions to get familiar with the basics of the scene graph and nodes. You can refer to the programmers' guide topic Scenes and Nodes or the INode entry in the C++ API Reference for detailed information about the INode class.
You can access the root node of the scene graph using the function Interface::GetRootNode(). Once you get a pointer to a node, there are many functions that get information about its scene object. A few examples of these functions are:
In the example plug-in for this lesson, we will create a scene export plug-in that writes information about all the nodes in the scene graph. In the plug-in wizard, select the 'File Export' plug-in type, and choose the 'Scene Export' for the base class. This class has a number of simple functions that return basic information about the scene exporter plug-in such as the type of the file to be generated, a description of what the exporter does, the number of file types supported, etc. The most important function that we need to implement is SceneExport::DoExport(). 3ds Max calls this function to do the main export job.
The project in the lesson folder Lesson2 shows the plug-in implementation. We use three helper functions named Lesson2::BeginWriting(), SampleExp::Write() and Lesson2::EndWriting() to open the file to be written to, write a message to the file and to close it. When our plug-in is selected to be used for export, 3ds Max takes care of the file selection UI and gives us the file name in its call to SceneExport::DoExport(). We implement a recursive private member function named Lesson2::Export() to help us exporting the scene graph information. This function takes a pointer to a node in the scene graph, writes its name and the number of its child notes in the text file, and then calls itself on each of the child nodes of the input node. This way all the nodes will have their name and the number of children written in the text file if we call this function for the root node. It will be a depth-first search since the recursive function is called on the children of the node right away after writing the node's info and before writing the information of the sibling nodes. The following code shows the implementation of this function:
void Lesson2::Export(INode* pNode, int iTreeDepth) { MCHAR * pNodeName = const_cast<MCHAR*>(pNode->GetName()); int nChildren = pNode->NumberOfChildren(); Write(iTreeDepth, "[%s] : %i children", pNodeName, nChildren); iTreeDepth++; for (int i = 0; i < nChildren; i++) { Export(pNode->GetChildNode(i), iTreeDepth); } }
The variable iTreeDepth in the above code stores the level of the current node and is used to format the output. The number of tabs printed at the start of each node info is the same as the node level. The lower in the scene graph a node is, more indented its info in the output file will be. Also the Lesson2::Export() is called on the root node in its first call in the Lesson2::DoExport() function:
int Lesson2::DoExport(const TCHAR *name,ExpInterface *ei, Interface *i, BOOL suppressPrompts, DWORD options) { ... INode* pRootNode = i->GetRootNode(); Export(pRootNode); ... }