Share

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 to Mesh::render() , otherwise see the other items on this list.
  • Do nothing if the plug-in's implementation of BaseObject::Display() calls Mesh::render() to display a wireframe (GW_WIREFRAME is set) unlit or un-textured mesh (GW_ILLUM or GW_TEXTURE are reset).
  • Otherwise, the following code changes are required:
    • Change the plug-in's implementation of BaseObject::Display() to skip calling Mesh::render() when the Nitrous viewport graphics driver is active - see MaxSDK::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 of BaseObject::Display() displays other than a mesh using Mesh::Render(), i.e. contains calls to the GraphicsWindow API, and false, if the plug-in's override of BaseObject::Display() skips all calls to the GraphicsWindow API when the Nitrous viewport mode is the active one (when MaxSDK::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 as IOBJECT_DISPLAY_INTERFACE_ID) to its BaseObject derived base class. Failure to do so can result in the plug-in not displaying properly in the Nitrous viewport graphics mode.

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 as LINE_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.

Was this information helpful?