Handling custom renderables for object overrides

This section focuses on MPxSurfaceShapeUI and MPxSurfaceShape which are found in Viewport 1.0 (default viewport), and the mapping of concepts to Viewport 2.0 interfaces. The focus is on MPxGeometryOverride and MPxSubSceneOverride, and any notable differences in approach between the two interfaces are highlighted.

The main goal is to show how custom renderables can be instantiated for “custom” drawing.

Viewport 1.0 only interfaces

The Viewport 2.0 API only provides a rendering / drawing interface.

Pre-existing Viewport 1.0 interfaces should be used for selection and component handling. This includes methods such as MPxSurfaceShapeUI::select() and MPxSurfaceShapeUI::snap().

Drawing into the UV Editor is unchanged. The existing Viewport 1.0 methods should be used, as there are no Viewport 2.0 specific interfaces for doing the same; for example, methods such as MPxSurfaceShapeUI::drawUV()/MPxSurfaceShapeUI::canDrawUV().

Viewport 1.0 methods for overriding associated materials are not required for Viewport 2.0 rendering. This includes MPxSurfaceShapeUI::material() and MPxSurfaceShapeUI::materials().

Renderables (render items)

The renderable is the main element of concern.

For Viewport 1.0, these are represented as draw requests (MDrawRequest). For Viewport 2.0, the closest construct to this is a render item (MRenderItem). A collection of these can be thought of as representing the total set of renderables for an object.

For Viewport 1.0, there are no predefined MDrawRequests.

For Viewport 2.0, MRenderItems that represent drawing using the assigned material(s) for an object are provided. We call these material render items. Anything not predefined by the renderer is considered to be custom.

The Viewport 1.0 paradigm is to create and return draw requests to the appropriate collection and then explicitly draw the requests at draw time. The Viewport 2.0 paradigm is to create, update and/or selectively override existing render items and return them to the appropriate collection for geometry update and eventual internal drawing.

The persistent nature of render items entails considering up-front creation of the total set of render items that will be used.

Provided render items

Viewport 2.0 renderer maintains material and bounding box render items.

It is possible, but not necessary, to override the shaders used for material render items. It is also possible to disable these render items and replace them with custom render items.

Note that the handling of objects that do not have any shader assignments is left up to the plug-in. In Viewport 1.0, the material interfaces return a default material. In Viewport 2.0, the fallback for non-plug-in objects is to show a wireframe. Because a default wireframe render item cannot be provided, this (or an alternative appropriate fallback) must be explicitly provided by the plug-in.

Custom render items

If only material and bounding box render items are provided, then the question arises as to when additional “custom” render items may be required. This is highly dependent on the type of plug-in being written, but one common situation is the rendering of non-material render items; another the overriding of existing material or bounding box drawing.

Non-material render items

In general, some minimal support for wireframe display mode drawing should be implemented for a custom shape. Anything else is dependent on the plug-in and the types of additional UI elements that must be drawn.

The rule-of-thumb is that: each additional variation of how or when to render should result in a new render item instance. A scan of MRenderItem shows the variations possible.

The following are some of the main variants:

  1. The render item is specific to a display mode (for example, wireframe versus shaded/textured modes). It is important to note that the display mode is matched against the current display modes used for rendering and suitably filtered out as necessary. For example: a shaded mode item is not drawn when the viewport is in wireframe mode.

    Figure 78

    A basic pipeline showing how draw mode filtering can potentially remove items from proceeding to the draw phase. This generally supersedes any filtering that is based on whether an item is considered to be “shaded” or “non-shaded”.

  2. The primitive type needs to be different. For example, one render draws lines and another draws points.
  3. The shader instance (MShaderInstance) needs to be different. For example, usage of a stipple line shader and a solid line shader would require different render items. Refer to 4.4.4.3 Choosing shaders and shader instances for more information.
  4. The shader parameter values need to be different. For example, two line shader instances need to have different colors. Refer to 4.4.4.3 Choosing shaders and shader instances for more information.
  5. The depth priority needs to be different.

    Figure 79

    Logical illustration of the effect of different depth priority settings that move the render item towards the active camera. “Custom” priorities can be used instead of stock ones. The arrow shows the direction of the camera (viewer).

  6. The streams of geometry data required by the shader need to be different. Variations include number of streams and stream description.

Pre-planning render item sets

It is advisable to pre-plan all the render items that could possibly be used at any given time during the interaction with the plug-in object. This working set of items help determine the dependent resources required, including: shaders, textures and geometry, and the amount of reuse that may be possible.

In general, the render item set remains fixed, with only parameter changes and enabling/disabling occurring for various items during the lifetime of the plug-in.

Having a large set of render items should not be a concern with respect to performance, given the assumption that variations are being created, and they are not all enabled at the same time. If large numbers of the same variation is required, then the plug-in writer may want to consider using MPxSubSceneOverride as opposed to an MPxGeometryOverride.

For UI drawing, it is best to minimize the number of overlapping elements to avoid overdraw cost. If this cannot be avoided, then it is best to determine the number of elements that may be enabled simultaneously. This drives the number of required render item variants with different depth priorities. For example, the drawing of an active line component on top of a dormant line component, on top of a wireframe requires three render items even though they may be identical in all respects except for depth priority.

In the case where render items are required per instance of a DAG object, it is worthwhile to keep in mind that different render items can be created per instance. It is up to the plug-in writer to maintain any explicit associations between items and instances.

Choosing shaders and shader instances

The options for choosing a shader for a render item can vary in complexity and application. Regardless of which option is chosen, it is best to separate shaders from geometry handling.

The simplest option is to use stock shaders provided via MShaderManager. These provide default configurations for shaders that can be used for point, line, and triangle UI drawing, as well as a few stock material shaders. Stock shader instances are specific to the primitive type; for example, a dashed line shader would be inappropriate for rendering triangles.

Using custom effect file shaders is also possible for greater control but requires implementations for all draw APIs (for example, DirectX11, and OpenGL). One reason for using custom shaders is to have a more complex set of parameters that can allow for dynamic updates as opposed to a larger set of static render item variants.

Fragment based shaders are best suited for integration with Maya's shading networks and are not recommended for UI drawing. Using built-in fragments is a simple way to get full shading and lighting support.

For the most flexibility, shaders should not be owned and controlled by geometry overrides but instead separate MPxShaderOverrides should be used. Thus geometry overrides are geometry handlers and shader overrides are material handlers.

Each shader has a set of parameters and geometry requirements associated with it. Each instance of a shader has different shader parameters and/or requirements, resulting in different render items for each shader instance.

Depending on the frequency of updates, it may be easier to attach callbacks to MShaderInstances for parameter updating. For example, this would allow changing the thickness parameter of a line shader during a callback, as opposed to creating two separate shader instances (hence render items) with different line thickness parameter values.

In the case where different streams are bound to the same set of geometry requirements, different shader instances are not required, as geometry binding is performed dynamically.

Handling change

Handling display status changes

By default, the plug-in gets called to update its UI render items when the display status of the associated DAG objects changes. This occurs in the following situations:

  • The object’s selection state changes: dormant / active / highlight, active-component are a few of the possible states.
  • The object's per object visibility or template status changes.
  • The object’s per object draw override options change.
  • The object display layer association changes or the options on associated display layer changes

Selection state changes generally imply a change in the color for a render item. There can be two approaches: either multiple render items can be created for each selection state; or, a single one with sufficient shader parameters can be used. If geometry is generally shared, then the inherent cost of having multiple items is small. If the draw mode differs, then a different render item is required. For example, a different item would be used for dormant versus active wireframe as the first only draws in wireframe mode, while the latter draws in all draw modes.

The actual control of colors can generally be handled by utilities such as MGeometryUtilities::wireframeColor(), which returns the correct color, taking into account current object state.

For object visibility, material render items are automatically handled, as are the UI render items. However, template status change can be considered to be a visibility change with respect to certain render items. For example, component render items should be manually disabled to be consistent with Maya’s internal behaviour. MGeometryUtilities::displayStatus() is a utility which is useful for checking status.

For draw overrides (exposed in the Attribute Editor Draw Overrides tab), template control needs to be handled as well as custom color overrides. The same utilities mentioned above may be used.

Display layers can invoke updates for templating, as well as color overrides.

Any additional items that are not controlled via Maya have its own internal plug-in logic and corresponding update logic. For example, adding the display of face centers could be an attribute on the node that drives the display of a custom render item to show these centers.

Handling of display mode changes

By default, an override is not called when the display mode for rendering changes. This is important to know as this means that all render items for all possible display modes need to be taken into consideration. The recommended approach is to create all render items with the appropriate parameters and handle any updates not related to display mode as required at “update” time.

An important difference between geometry overrides and sub-scene overrides is that sub-scene overrides receive constant update calls where a frame context is provided. In this case, render items can be created or modified on-demand.

General state monitoring

For general events that occur in Maya, it is up to the plug-in to determine which events to monitor and determine if they require a custom render item update. For example, view-dependent render items need to perform their own camera monitoring (for example, via MUIMessage).

Geometry handling

Custom render items are named and thus there is a set of named render items to manage. There is also the possibly of the requirement for geometry streams of the same semantic but with different names.

For example, two streams of positions (semantic) may be required for the case where a material render item draw and a render item that draws face normal are required. The first could be named “My shaded item” and the second “My face center item”. The latter could call MRenderItem::setShader() with a custom stream name of “face center”.

Additionally, as no explicit association is given between streams and render items, it can sometimes be useful to name streams if it is required that they be used for specific custom render items. There are trade-offs, as each named stream may result in additional geometry data and indexing allocation being required. Also, geometry updates must handle the fact that stream naming is per render item. That is, it applies to all streams for an item. An example usage is given in 4.4.5.5 Basic component handling.

Figure 80

The figure above shows three render items on the left. The top-most item is for drawing face centers. It requires only positions and has a custom name of "face center". Next is an item to draw active vertices and requires positions and has a custom stream name of "Active Vertices". The last is a material render item which requires positions and normal. There are no custom names. When the final cumulative set of requirements comes back for which the override must provide geometry, there are descriptions for 4 streams of data required (MVertexBufferDescriptor): 3 position streams and one normal stream. The position streams could be shared for material and active vertex display.

No assumptions should be made as to when data is required, and as such, all requirements should always be fulfilled. The renderer may attempt to provide some default data if the appropriate data is not provided, but there is no guarantee that the appropriate data can be provided. For example, one viewport panel may require the data for a wireframe render item in addition to a shaded one if the viewport configuration has one panel in shaded mode and the other with wire-on-shaded mode enabled. The shaded render item may require tangents to be provided.

Basic component handling

When components have been modified, an override should extract the appropriate component information from the plug-in object at DG evaluation time. For example, component indexing could be cached. (For MPxGeometryOverride, this is during updatedDG()). This indexing can be used to provide the primitive indexing for the render items associated with the supported component. For example, for a single indexed component such as vertices, the vertex identifiers could be cached.

For each component type, the override can have one or more render items created depending on the display variations required (For MPxGeometryOverride, this is during updateRenderItems()). In the aforementioned vertex component example, there can be one item for displaying unselected vertices and one item for selected vertices. Each, for example, can have a different color, size and/or depth priority (if they overlap).

A stock shader instance appropriate for point drawing could be associated with each render item. The unselected vertex render item would only apply when in wireframe mode so that draw mode should be associated with it. The selected vertex render item would be set to draw in shaded, texture and wireframe modes since it can be seen in all of these modes. By default, the items would be enabled when vertex display is enabled, or when the object’s display status is in “hilite” mode.

At geometry update time (for MPxGeometryOverride, this is during populateGeometry()), the appropriate stream data is requested based on the shader geometry requirements. Depending on the component indexing evaluated previously, the appropriate primitive indexing needs to be created/updated.

For sub-scene overrides, the entire transaction would be handled in the update() method.

As an example of using named streams, one position stream may be used for unselected drawing and another for selected vertex drawing. On the plus side, the number of selected vertices could be small resulting in reduced indexing and data transfer. On the minus side, an additional stream needs to be allocated, increasing memory cost. Re-using the same stream would mean re-using the same memory, with the potential for complex indexing computation and overdraw.

Custom UI drawing example

As an example, a plug-in can create custom render items for the following:

Figure 81

Green boxes represent areas that the plug-in would create and manage. White boxes are provided by the renderer.

The following are example parameters of the items illustrated above and their proposed update frequency:

The crescent “container” shows how logically both geometry and subscene override render items are collected into a set of render items. The main difference is that subscene override container classes are explicitly exposed in the API.

The following are some example user workflows and how the plug-in could handle their render items:

  1. User goes into component selection mode while in shaded mode:
    1. The object is in highlight mode so the dormant wireframe item should be enabled and the color set appropriately.
    2. The dormant vertex render item should be enabled and the color set appropriately.
    3. The active wireframe item should be disabled.
    4. The active vertex item should be disabled.
    5. The shaded template item should be disabled.
    6. The shaded proxy item should be disabled.
      NOTE: The material shaded item is enabled internally, and was enabled when entering into shaded mode.
  2. The user then selects some vertices:
    1. The active vertex item should be enabled. The active components should be parsed to set the appropriate indexing to show a subset of vertices.
  3. The user switches back to object selection mode:
    1. Dormant and active vertex items are disabled.
    2. Active wireframe item is enabled.
    3. The dormant wireframe item is disabled.
  4. The user switches to wireframe mode:
    1. “Nothing” needs to be done in the plug-in as the render items marked to draw in shaded mode are filtered out in the draw pipeline.
  5. The user sets the object override to make the object template:
    1. If there are any custom material render items, they should be disabled. Provided material render items are automatically disabled.
    2. The dormant and active wireframe items should be disabled.
    3. The dormant vertex item should be disabled.
    4. The shaded template item should be enabled and set to the appropriate color depending on whether or not the object is active.

Figure 82

From top to bottom, and left to right are the five actions listed above, shown graphically. At all times, all the render items are retained with basic enabling and disabling occurring as well as possible parameter changes such as color. Items in green are enabled while all other render items are disabled.

For geometry overrides, the majority of the handling is done in updateRenderItems(), for sub-scene overrides in update(). The SDK plug-in apiMeshShape contains example code which contains logic to support workflows like the above, as well as many “standard” workflows.

Custom materials

If custom material rendering is required, then the level of integration is dependent on the interfaces that are used. The following table outlines the main trade-offs associated with each interface. Increased flexibility generally requires more work to be done by the plug-in writer. To a certain extent, simpler shaders can be maintained inside the geometry handler/override.

Fragment, Effects, and Stock are options available via the shader manager, and hence supported options are determined by the internal renderer. Shader Override is MPxShaderOverride.

  Fragment Shader Effects Shader Stock Shader Shader Override
Flexibility : Complexity ratio High : High Medium : Low Low : Low High : High
Use case

Generally used to enhance the set of existing fragments.

Can be used as “stock” shader but more complex to set up appropriate shading network.

All internal semantic bindings supported.

Self-contained effect which can use default semantic bindings.

Simple built in effect is “good enough”.

Useful when plug-in does not want to deal with writing complex UI shaders to replace any previously easy-to-write fixed-function code.

Complex shading support is required beyond what the internal renderer can provide; for example, tessellation shaders.
Render item “type” Custom. Custom. Custom. Non-Custom.
Shading graph support

If using individual fragments, none.

If the fragment shader is used in a node as part of a Maya shader tree, then it is fully integrated. If the fragment shader is used to create an MShaderInstance then there is no explicit support.

None. None. Plug-in determined.
Lighting support Integrated. No automatic binding. Ignored by design. Plug-in binds via access to draw context.
Post effects support Integrated. Not integrated. Ignored by design. Plug-in provides techniques to support various passes required by effects.
Draw API Support If custom, requires implementation for each draw API, otherwise API agnostic. Plug-in writer writes versions for APIs to support. Built-in. Plug-in writer writes versions for APIs to support.
Shader stages supported Vertex, Pixel. Vertex, Pixel, Geometry. Not exposed. Determined by plug-in.
Separation from geometry handler (override) Can be used for shader instances on render items created by geometry handler. Can be used for shader instances on render items created by geometry handler. Can be used for shader instances on render items created by geometry handler. Independent from geometry handling.

When dealing with material items, there are generally render items that have “shaded” or “shaded and textured” draw modes being passed to an override. There can be 0 or more sets of these passed in, depending on the number of materials that are assigned to the DAG object.

Figure 83

In this example, a DAG object has 2 shaders assigned to different components on the DAG object. It is possible to have 4 “material” render items: one for each component shader to draw in “shaded” draw mode, and one for each component shader to draw in “shaded and textured” mode.

Parameters on these render items can be used as is, modified, or they can be disabled. If disabled, then the plug-in is responsible for providing replacements. If no replacement is provided, then the object appears to “disappear” when in shaded and/or textured modes. Depending on the type of replacement, different levels of integration may result.

When the shader handling complexity for certain render items reaches a point where an MPxShaderOverride is required, then the complexity of the geometry handler is reduced, and it only fulfills the geometry requirements for these render items.

For example, the plug-in may require only a built-in Blinn shader with some pre-set shading configuration that is fully integrated. A stock fragment shader can be used in this case. In another example, tessellation may be required for a hair shader. In this case, it is best to not control this from inside the geometry handler and instead create a custom MPxShaderOverride “hair shader node” which “feeds” render items to a custom “hair geometry node” override.

Figure 84

The various possible options are all shown at once.

The renderer provided (material) render item is shown uncoloured. There could be a mix of internal shaders and plug-in shaders used to derive these items. Some nodes may supply a custom fragment shader or a shader override may be associated. The geometry handler (override) receives these as non-custom render items.

Custom render items (in green) can be instantiated with shader instances which are created via the shader manager using fragment, stock or effects files interfaces.

UI Draw Manager

The MUIDrawManager class provides a convenience interface for queuing additional UI drawing that are generally not associated with being part of an object. The “drawables” which may be queued can be thought of as being roughly equivalent to render items.

A direct implication of using the manager is that the “drawables” are never persistent and as such, the number of drawables queued via this interface should be kept relatively small. If more items are required or the data content is large, then interfaces such as the mesh() call be used. Custom render items are otherwise recommended.

For example, for drawing a large number of vertices on a plug-in object, a custom render item would be recommended over drawing individual points in the draw manager. However for drawing a 3d or 2d label on an object, the draw manager would be a better choice.

One other aspect to consider is that the UI drawn via the manager may draw at a fixed place within the overall pipeline and with a more rigid set of pre-defined drawing properties.

Effects and context interaction

By default, render items used for UI drawing (lines, points) are excluded from scene effects such as screen-space ambient occlusion, motion blur, and depth-of-field. Additionally, they do not, in general, cast or receive shadows. Render items using internal material shader instances participate in these effects.

Effects drawing is performed within some drawing context.

Geometry overrides are never passed any context information and their behaviour is for the most part independent of context (shader callbacks being the exception). Subscene overrides are provided with context but only a frame context which does not provide access to a pass context. Thus, any items should work independently of the pass from which they are being called.

Viewport 1.0 / Viewport 2.0 comparison chart

  Viewport 1.0 Viewport 2.0
Custom renderable creation MDrawInfo::getPrototype() MRenderItem::Create()
Custom renderable deletion Draw requests are transient and released internally every refresh. MRenderItem::Destroy()
Queuing renderables MPxSurfaceShapeUI::getDrawRequests()

MPxGeometryOverride::updatedDG() and MPxGeometryOverride::updateRenderItems().

MPxSubSceneOverride::update()

Rendering MPxSurfaceShapeUI::draw() Not applicable. Rendering is done by the renderer.
Material handling Template fixed function material provided. Can be overridden at will.

MShaderInstance based. Parameters modifiable at update time. Shader binding (rebinding) to render items possible.

More complex / decoupled material handling should be done via MPxShaderOverride.

Display Mode Handling Display mode is provided as input and can be ignored at will. Draw modes are intrinsic part of a render item. Internal logic prune items based on display mode membership.
Geometry Requirements Interface No concept.

MPxGeometryOverride::populateGeometry()

MPxSubSceneOverride::setGeometryForRenderItem()

Geometry Requirements Filling No concept. Explicit stream naming possible for different render items resulting in additional buffer and index handling.
Bounding box draw Internally controlled. Internally controlled.
Ghosting Control Multiple draw calls are made for single set of draw requests. Plug-in controlled. Single set of calls for all ghosts.
Shadow Casting / Receiving Control via the "cast" and "receives" attributes on the object, if they exist. Otherwise, no control. Control per custom render item, as applicable.
Display Status Query Display status information passed in as part of MDrawInfo. Display status can be queried from MDagPath, MGeometryUtilities.
Component Management Passable to draw requests. Plug-in performs management. Component information can be passed to draw with appropriate render items. Viewport 1.0 interfaces perform component management.
Visibility Handling Prune requests when queuing draw requests or drawing. Plug-in enables / disables render items as required, or sets draw mode to use internal draw mode filtering.
UV Rendering MPxSurfaceShapeUI::drawUV() No concept. Uses Viewport 1.0 interface.
Selection MPxSurfaceShapeUI::select() No concept. Uses Viewport 1.0 interface.
Selection Status Query Information found in the MDrawInfo class indicates if drawing is occurring for the selection. Selection is not part of Viewport 2.0, so not applicable.
User interaction feedback Some information is available via MDrawInfo. Plug-in explicitly monitors for user events of interest.

Sample code

The apiMeshShape developer kit example takes into account a number of workflows related to UI handling as well as for proxy shader handling.

Figure 85

apiMeshShape sample scene in wireframe mode.

Figure 86

apiMeshShape sample scene in shaded mode.

The above snapshots demonstrate a number of different custom render items being used. All objects except the planes and the IK chain are drawn using the same plug-in. Included are:

Sample code for active wireframe handling

The following code demonstrates the handling of an “active wireframe” render item from within MPxGeometryOverride::updateRenderItems(). The code logic would be similar if used with MPxSubSceneOverride, except that the render item would belong to the container (MPxSubSceneContainer) instead of an MRenderItemList.

The following is an example method used to set the color parameter on a shader:

// Utility to set the color on a shader instance.
void setSolidColor(MHWRender::MShaderInstance* shaderInstance, const float *value)
{
  if (!shaderInstance)
	return;
  const MString colorParameterName = "solidColor";
  shaderInstance->setParameter(colorParameterName, value);
}

During updateRenderItems(), a new “active” wireframe item is created if it does not already exist. If a new wireframe is created, then the draw mode is set to “all” modes and the item is set to draw above any shaded render item.

// ************************** Update during updateRenderItems() ******************
…
// Unique name for active wireframe
MString fSelectedWireframeItemName = “selectedWireframeName”;
// Create the item once. Use enable/disable toggle to control when it should
// be drawn (below)
MHWRender::MRenderItem* selectItem = NULL;
int index = list.indexOf( // list = MRenderItemList passed as an input argument
	fSelectedWireframeItemName,
	MHWRender::MGeometry::kLines, // Type of primitive is lines 
	MHWRender::MGeometry::kAll); // Draw in all display modes
if (index < 0)
{
	static const bool raiseAboveShaded = true;
	selectItem = MHWRender::MRenderItem::Create(
		fSelectedWireframeItemName,
		MHWRender::MGeometry::kLines,
		MHWRender::MGeometry::kAll,
		raiseAboveShaded);
	// This is the same as setting the argument raiseAboveShaded = true,
	// since it sets the priority value to be the same. This line is just
	// an example of another way to do the same thing after creation of
	// the render item.
	selectItem->depthPriority( MHWRender::MRenderItem::sActiveWireDepthPriority);
	list.append(selectItem);

	// Choose an line shader to match primitive type
	MHWRender::MShaderInstance* shader = 
		shaderMgr->getStockShader( MHWRender::MShaderManager::k3dSolidShader );
	if (shader)
	{

		// Assign shader
		selectItem->setShader(shader);
		// Once assigned, no need to hold on to shader instance
		shaderMgr->releaseShader(shader);
	}
}
else
{
	selectItem = list.itemAt(index);
}

Based on the display status, during each update, the color for the wireframe item is updated. It switches colors based on statuses such as lead, active, highlight, and active-component.
// Perform updates on the render item parameters. In this case
// update the shader instance.
//
MHWRender::MShaderInstance* shader = NULL;
if (selectItem)
{
	shader = selectItem->getShader();
}

// Check the current display status of the object and color to use for this
// instance of the object.
MHWRender::DisplayStatus displayStatus = 
	MHWRender::MGeometryUtilities::displayStatus(path);
MColor wireColor = MHWRender::MGeometryUtilities::wireframeColor(path);

// It is not necessary to use the colors provided and instead a custom color could be 
// used if (fUseCustomColors) is set.
bool fUseCustomColors = false;
switch (displayStatus) {
case MHWRender::kLead:
	selectItem->enable(true);
	if (shader)
	{
		static const float theColor[] = { 0.0f, 0.8f, 0.0f, 1.0f };
		setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : theColor );
	}
	break;
case MHWRender::kActive:
	if (shader)
	{
		static const float theColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
		setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : theColor );
	}
	selectItem->enable(true); // Enable if the object is active
	break;
case MHWRender::kHilite:
case MHWRender::kActiveComponent:
	if (shader)
	{
		static const float theColor[] = { 0.0f, 0.5f, 0.7f, 1.0f };
		setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : theColor );
	}
	selectItem->enable(true); // Enable if the object is hilite, or 
                                  // its components are active.

	break;
default:
	// Otherwise make sure to disable it. The item is still present, it will just
	// not be sent down the rendering pipeline to draw.
	selectItem->enable(false);
	break;
};

Sample code for active vertex handling

The following is sample code for handling of an “active vertex” render item from within MPxGeometryOverride::updateRenderItems(). There is a separate utility method which checks display status to determine whether or not the component render item should be disabled.

The test also considers if there are any active vertices, which is determined during DG update time (updatedDG()).

Note that this code path has been written to allow for named streams to be used. Inside the populateGeometry() method, these named streams can be checked and a different set of positions can be used to draw active vertices with.

The following code executes during DG update time. It gets the component list from the object and caches the element IDs in an integer array.

/***** Call in MPxGeometryOverride::updatedDG() to perform any DG operations ****/
void updateDG()
{
	fActiveVertices.clear(); // fActiveVertices is an MIntArray 
	
	MObjectArray clist = // Get the active vertex components – plug-in specific ;
	if (clist.length())
	{
		MFnSingleIndexedComponent fnComponent( clist[0] );
		if (fnComponent.elementCount())
		{
			// Cache the vertex identifiers for later usage.
			fnComponent.getElements( fActiveVertices );
		}
	}
}
The following utility tests to determine whether or not to hide components:
/* Test to see if active components should be enabled.
           Based on active vertices + non-template state
*/
bool enableActiveComponentDisplay(const MDagPath &path) const
{
	bool enable = true;

	// If no active components then disable the active
	// component render item
	if (fActiveVertices.length() == 0)
	{
		enable = false;
	}
	else
	{
		// If there are components then we need to check
		// either the display status of the object, or
		// in the case of a templated object make sure 
		// to hide components to be consistent with how
		// internal objects behave
		//
		MHWRender::DisplayStatus displayStatus = 
			MHWRender::MGeometryUtilities::displayStatus(path);
		if (displayStatus == MHWRender::kTemplate ||
			displayStatus == MHWRender::kActiveTemplate)
		{
			enable = false;
		}
		else
		{
			// Do an explicit path test for templated
			// since display status does not indicate this.
			if (path.isTemplated())
				enable = false;
		}
	}
	return enable;
}

During updateRenderItems(), the appropriate item is created as needed to draw in "all" modes. The depth priority is set to avoid being obscured by dormant vertices and any wireframe items. The item is enabled based on whether or not components should be displayed.

…
// Unique name identifier for the render item
MString fActiveVertexItemName = “activeVertexItemName”;
MHWRender::MRenderItem* activeItem = NULL;
int index = list.indexOf(
	fActiveVertexItemName,
	MHWRender::MGeometry::kPoints,// Primitive type is points.
	MHWRender::MGeometry::kAll); // Render item should display in all display modes.
if (index < 0)
{
	activeItem = MHWRender::MRenderItem::Create(
		fActiveVertexItemName,
		MHWRender::MGeometry::kPoints,
		MHWRender::MGeometry::kAll,
		false);
	// Set depth priority to be active point. This should offset it 
	// to be visible above items with "dormant point" priority.
	activeItem->depthPriority( MHWRender::MRenderItem::sActivePointDepthPriority );
	list.append(activeItem);

	MHWRender::MShaderInstance* shader = shaderMgr->getStockShader(
		MHWRender::MShaderManager::k3dFatPointShader );
	if (shader)
	{
		// Set the point size parameter. Make it slightly larger for active
		// vertices
		static const float pointSize = 5.0f;
		setSolidPointSize( shader, pointSize );

		// Assign shader. Use a named stream if we want to supply a different
		// set of "shared" vertices for drawing active vertices
		bool fDrawSharedActiveVertices = true;
		MString fActiveVertexStreamName = “activeVertexStreamName”;
		if (fDrawSharedActiveVertices)
		{
			activeItem->setShader(shader, &fActiveVertexStreamName );
		}
		else
		{
			activeItem->setShader(shader, NULL);
		}

		// once assigned, no need to hold on to shader instance
		shaderMgr->releaseShader(shader);
	}
}
else
{
	activeItem = list.itemAt(index);
}

// Update the color and enable / disable the render item as appropriate.
// Just using a fixed color and not checking display status.
bool enableActiveDisplay = enableActiveComponentDisplay( <dagpath> );
if (activeItem)
{
	MHWRender::MShaderInstance* shader = activeItem->getShader();
	if (shader)
	{
		// Set active color
		static const float theColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
			setSolidColor( shader, theColor); // See code for active 
					// wireframe for this code utility.
	}

	activeItem->enable( enableActiveDisplay );
}

The following is part of the code logic for handling the custom named stream in populateGeometry(). The key part of the code is the checking of the vertex buffer descriptor name to see if it matches the name set when calling MRenderItem::setShader() during updateRenderItems() above. The plug-in is then free to fill in the data, and create the appropriate indexing structure for the render item with the name matching the value stored in the variable: fActiveVertexItemName.

const MHWRender::MVertexBufferDescriptorList& descList =
		requirements.vertexRequirements();
int numVertexReqs = descList.length();
MHWRender::MVertexBufferDescriptor desc;
for (int reqNum=0; reqNum<numVertexReqs; reqNum++)
{
	if (!descList.getDescriptor(reqNum, desc))
	{
		continue;
	}

	// Fill in vertex data for drawing active vertex components 
	//
	if (fDrawSharedActiveVertices && (desc.name() == fActiveVertexStreamName))
	switch (desc.semantic())
	{
		case MHWRender::MGeometry::kPosition:
		{
			if (!activeVertexPositionBuffer)
			{
				activeVertexPositionBuffer = data.createVertexBuffer(desc);
				if (activeVertexPositionBuffer)
				{
				   // Allocate a position buffer to fit the # of active vertices
				   unsigned int activeVertexCount = fActiveVertices.length()
				   activeVertexPositions =
				      (float*)activeVertexPositionBuffer>acquire(
				              activeVertexCount, 
				              true 
				              /*writeOnly*/				
				}
			}
		}
		break;

		default:
			break;
	}
}