Porting Object Plug-ins to Nitrous
Object Display API
The object display API is a retained mode graphics API whose functionality is similar to the cached mesh support found with the DirectX viewport driver. This means that plug-ins describe the data to be displayed in the viewport and update it when needed in a declarative manner. The graphics driver stores an internal representation of this data that it retains from frame to frame and uses for rendering to the viewports. This design allows the graphics driver to optimize the rendering and related processing, and to perform rendering in a separate thread. A retained mode graphics API contrasts with an immediate mode graphics API where the drawing commands are immediately rendered.
The Nitrous display driver provides a compatibility layer for plug-ins developed using the legacy immediate mode GraphicsWindow
API. As a result, most plug-ins will not require code changes in order to display in the Nitrous viewport display mode. The cases when plug-in code needs to be changed are described in the following section, Impact on Plug-ins.
The object display API consists of the interface class IObjectDisplay
. The Nitrous graphics driver queries plug-ins for this interface in order to get their viewport representation as RenderItemHandles
(see IObjectDisplay::GetRenderItems()
) and asks them to update it (see IObjectDisplay::UpdateDisplay()
). The BaseObject
class - which is the base class for all procedural objects and modifiers - exposes the IObjectDisplay
interface and provides storage for render item handles (see BaseObject::mRenderItemHandles
). It means that even those plug-ins that require changes to their BaseObject::Display()
function in order to be displayed correctly in the Nitrous viewport display mode, will only need to override the BaseObject::UpdateDisplay()
function.
Impact on Plug-ins
Most procedural object plug-ins are supported as-is by the Nitrous viewport display driver, except for those that have a lit or textured mesh representation and use Mesh::render()
in their implementation of BaseObject::Display()
. In these cases, the mesh will display flat shaded and unlit in the Nitrous viewport, but any other part of the plug-ins viewport representation drawn using the GraphicsWindow
API will display correctly. If the plug-in explicitly sets the GW_WIREFRAME
flag and resets GW_ILLUM
or GW_TEXTURE
, then the mesh will render correctly in the Nitrous viewport since no lighting or texturing information needs to be translated from the immediate mode graphics driver to the retained mode one.
Plug-in Porting Checklist
- Do nothing if the plug-in's implementation of
BaseObject::Display()
doesn't make calls toMesh::render()
, otherwise see the other items on this list. - Do nothing if the plug-in's implementation of
BaseObject::Display()
callsMesh::render()
to display a wireframe (GW_WIREFRAME
is set) unlit or un-textured mesh (GW_ILLUM
orGW_TEXTURE
are reset). - Otherwise, the following code changes are required:
- Change the plug-in's implementation of
BaseObject::Display()
to skip callingMesh::render()
when the Nitrous viewport graphics driver is active - seeMaxSDK::Graphics::IsRetainedModeEnabled()
. Leave the rest of the implementation of the Display member function unchanged. - Override
BaseObject::RequiresSupportForLegacyDisplayMode()
to return true if the plug-in's override ofBaseObject::Display()
displays other than a mesh usingMesh::Render()
, i.e. contains calls to theGraphicsWindow
API, and false, if the plug-in's override ofBaseObject::Display()
skips all calls to theGraphicsWindow
API when the Nitrous viewport mode is the active one (whenMaxSDK::Graphics::IsRetainedModeEnabled()
returns true). - Override
BaseObject::UpdateDisplay()
to generate the plug-in's mesh representation and the render items based on that. - Add GraphicsDriver.lib and DataBridge.lib as a linker dependency to the plug-in's project settings.
- Important: The plug-in's override of the
InterfaceServer::GetInterface(Interface_ID iid)
virtual function must route requests for all interfaces not directly implemented by the plug-in (such asIOBJECT_DISPLAY_INTERFACE_ID
) to itsBaseObject
derived base class. Failure to do so can result in the plug-in not displaying properly in the Nitrous viewport graphics mode.
- Change the plug-in's implementation of
Sample Code
Following is the ShapeObject
display code before the upgrade to support Nitrous viewport graphics mode:
//ShapeObject display code before the upgrade to support Nitrous viewport graphics mode.
int ShapeObject::Display(TimeValue t, INode *inode, ViewExp* vpt, int flags)
{
if (!GetDispRenderMesh())
return 0;
// Create a mesh to display and cache it
GenerateMesh(t, GENMESH_DEFAULT, NULL);
GraphicsWindow *gw = vpt->getGW();
// Call Mesh::render() to display the mesh
meshCache.render( gw, inode->Mtls(),
(flags&USE_DAMAGE_RECT) ? &vpt->GetDammageRect():NULL,
COMP_ALL | (inode->Selected()?COMP_OBJSELECTED:0), inode->NumMtls(),
((ReferenceTarget*)this));
return 0;
}
Following is the ShapeObject
display code after the upgrade to support Nitrous viewport graphics mode:
//After the upgrade to support Nitrous viewport graphics mode.
int ShapeObject::Display(TimeValue t, INode *inode, ViewExp* vpt, int flags) {
if (MaxGraphics::IsNitrousGraphicsEnabled())
return 0;
if(!GetDispRenderMesh())
return 0;
// Create a mesh to display and cache it
GenerateMesh(t, GENMESH_DEFAULT, NULL);
GraphicsWindow *gw = vpt->getGW();
meshCache.render( gw, inode->Mtls(),
(flags&USE_DAMAGE_RECT) ? &vpt->GetDammageRect():NULL,
COMP_ALL | (inode->Selected()?COMP_OBJSELECTED:0), inode->NumMtls(),
((ReferenceTarget*)this));
return 0;
}
bool ShapeObject::UpdateDisplay(const MaxSDK::Graphics::MaxContext& maxContext,
const MaxSDK::Graphics::UpdateDisplayContext& displayContext)
{
using namespace MaxSDK::Graphics;
if (!GetDispRenderMesh()) {
mRenderItemHandles.ClearAllRenderItems();
return true;
}
// create a mesh to display (leave it in cache)
GenerateMesh(displayContext.GetDisplayTime(), GENMESH_DEFAULT, NULL);
GenerateMeshRenderItemsContext generateRenderItemsContext;
generateRenderItemsContext.GenerateDefaultContext(displayContext);
meshCache.GenerateRenderItems(mRenderItemHandles,generateRenderItemsContext);
return true;
}
Known Issues
- Compound objects may not display correctly - Objects that have a branched geometry pipeline may not have all of their branches displayed correctly in the Nitrous viewport. This can happen, for example, when the Display method of a certain branch changes some of the parameters of
GraphicsWindow
such asLINE_COLOR
(GraphicsWindow::SetLineColor()
). - Object may fail to display correctly - If an object's mesh depends on parameters whose changes do not trigger notifications the Nitrous graphics driver is aware of, the mesh will not update in the Nitrous viewport since
BaseObject::UpdateDisplay()
won't be called for that object.