Using versions of the 3ds Max SDK prior to Version 8, when a developer needed to make a regular reference that would have led to a circular reference, there
was no choice but to use a pointer to a node instead. Pointers are not managed by the Reference System and thus not stored properly to Max files. This meant that entities created by plugins that used node pointers would cause 3ds Max to crash when the plug-in
data was XREF'd and the XREF loaded. The only work-around was to avoid XREFing stored data for those plugins.
Developers can now use Indirect References in situations where Regular References would create circular references.
This topic provides the steps for converting node pointers to Indirect References and testing the resultant code. Also see the topic Indirect References and Object XRefs for more information.
Conversion Steps
A. Add a NodeMonitor to the plug-in as a new data member
- Declare a new ReferenceTarget* member in the plug-in class and in the plug-in's ctor create and instance of it by calling
Interface::CreateInstance(REF_TARGET_CLASS_ID, NODEMONITOR_CLASS_ID)
- Add two public methods to the plug-in class for setting and getting the node
- Replace usage of the node pointer with set\get methods described above
B. Modify Plugin to Reference the NodeMonitor object
- Define new refids and modify the NumRefs, SetReference, GetReference methods to report and work with the new reference
C. Modify the Clone method to also clone the NodeMonitor
- Clone the node monitor in the same way it clones other references the plug-in makes using the RemapDir parameter passed to it
D. Modify File IO Methods
1. Modify the Save method to:
- Save out a version chunk
- Not save the pointer to the 3ds Max file
2. Modify the Load method to:
- Load legacy versions of the plug-in (the ones with no version chunk but with a pointer saved to the 3ds Max file), and register
a postload-callback that would transform that pointer into an indirect reference. (The postload-callback would set the node
in the NodeMonitor. maxsdk\samples\objects\extendedobjects\hose.cpp and maxsdk\samples\systems\sunlight\sunlight.cpp are examples of this procedure.
- Load the new version of the plug-in (the one with the version chunk and without the pointer saved to the 3ds Max file)
- Define a postload-callback class that would transform the raw node pointer read from a legacy 3ds Max file, into a Node Monitor
and register this PLC from within the load method that loads legacy files
E. Create a postload-callback class
- The NodeMonitor is set-up to point to the node pointer read from legacy files using a postload-callback. (The other place
where the NodeMonitor is initialized with the node is usually a creation procedure.)
- In order to ensure that the reference held by the NodeMonitor is remapped correctly when this plug-in is XREF'd, the postload-callback
also needs to record a node-ref-remap item with the ILoad object passed into the postload-callback's procedure (PostLoadCallback::proc). Use ILoad::RecordNodeRefRemap to record remap information for Indirect References to nodes. For more information, see ioapi.h.
F. Undo\Redo
- A NodeMonitor has built-in undo/redo support. This means that, in general, your plug-in does not need to do anything special about undoing/redoing
the monitoring of nodes.
Unit Testing
A. Ensure the new version of the plug-in object works
- Create plug-in object (undo\redo\undo)
- Delete plug-in object (undo\redo\undo)
- Clone the plug-in object (undo\redo\undo)
- Persist the plug-in object (save\load\merge)
- Object and Scene XREF the plug-in object, reloading the xref, using nested XREFing, etc
- Plugin specific tests - white box tests based on the the code where the node pointer was being used
B. Ensure legacy versions of the plug-in object work
- Load max files with the legacy version of the plug-in object, then delete, clone, render, work with the object
- Save the scene, then load it again
- Object\Scene XREF 3ds Max files with the legacy version of the plug-in object
- Reload the XREF