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). That means that even those plug-ins that are 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

Sample Code

The following code illustrates the ShapeObject's support for the Nitrous viewport graphics mode.

//
// ShapeObject before 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;
}

//
// ShapeObject after upgrade to support Nitrous viewport graphics mode.
//
int ShapeObject::Display(TimeValue t, INode *inode, ViewExp* vpt, int flags) 
{
	// Skip the legacy mesh display if the Nitrous viewport display mode is enabled.
	if (MaxSDK::Graphics::IsRetainedModeEnabled()) 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::RequiresSupportForLegacyDisplayMode() const 
{
	return false;
}

bool ShapeObject::UpdateDisplay(
	unsigned long renderItemCategories, 
	const MaxSDK::Graphics::MaterialRequiredStreams& materialRequiredStreams, 
	TimeValue t) 
{
	if (!GetDispRenderMesh())
	{
		// Remove all render items if there's nothing to display
		mRenderItemHandles.removeAll();
		// Return true if render items changed, false otherwise.
		return true;
	}

	// Create a mesh to display and cache it
	GenerateMesh(t, GENMESH_DEFAULT, NULL);

	MaxSDK::Graphics::GenerateRenderItems(
		mRenderItemHandles, 
		&meshCache, 
		renderItemCategories, 
		materialRequiredStreams);

	return true;
}

Known Issues