MPxShaderOverride Class Reference


Detailed Description

Base class for user defined shading effect draw overrides.

MPxShaderOverride allows the user to create a custom override for associating a "full shading effect" with a shading node (custom or standard) in Maya. Its primary use is for associating hardware effects with pre-existing plugin software shaders.

A "full shading effect" defines the complete shading and lighting involved to render a given object. Input resources for shading such as geometry, textures, and lights are defined and bound to the shading effect via the override as required. The override is fully responsible for these tasks.

As an example, for hardware shading, this can be thought of as implementing a CgFx or HLSL effect file renderer which can use the resources defined within a Maya scene.

There are three main phases that the override must implement:

1) Initialization Phase : This phase occurs when Maya determines that the hardware shader generated through this override needs to be rebuilt. This may be due to a new object using the shader, or because something has changed on the shader that requires a complete rebuild. During the initialization phase the geometric stream requirements can be specified. If an MShaderInstance is used as the shader for rendering then setGeometryRequirements() should be called to extract the requirements from the MShaderInstance. Otherwise, addGeometryRequirement() or addGeometryRequirements() should be used. The requirements will determine which geometric streams are required from objects to which the given shading effect is assigned. If no requirements are specified then a single position stream requirement will be used.

The initialize() method must return a string representing the shader key. It often happens that different instances of the MPxShaderOverride represent essentially the same shader, but with different shader parameters. The shader key is used to identify the MPxShaderOverride instances representing the same shader. To optimize rendering, the Viewport 2.0 will make an effort to regroup the rendering of objects with the same MPxShaderOverride shader key. This allows the plug-in to perform its setup only once for the entire sequence. It is up to each plug-in to decide what the meaning of representing the same shader is.

There are two possible versions of initialize() to implement (however it is only necessary to implement one, the default implementation of the second calls the first). The first simply receives the shader node that is being initialized. The second receives additional context (such as the DAG path for the object bound to the shader) and also allows the implementation to send back custom user data to attach to the MRenderItem representing the DAG-object/shader association. The custom user data must be derived from MUserData. If the deleteAfterUse() method returns true for the MUserData instance then the data object will automatically be deleted when the render item is deleted. Otherwise the lifetime of the user data object is the responsibility of the caller. This user data may be accessed in the draw phase from the render item being drawn. Note that Maya objects which have render items that have custom user data can only be consolidated when the user data pointer on each item references the same user data instance. Draw performance will suffer without consolidation, however if the user data object is shared then memory management must be handled manually (ie. deleteAfterUse() should return false).

2) Data Update Phase : In this phase, updating of all data values required for shading is performed. The interface has an explicit split of when the dependency graph can be accessed (updateDG()), and when the draw API (e.g. OpenGL) can be accessed (updateDevice()). Any intermediate data can be cleaned up when endUpdate() is called.

As an example the override may require input from an attribute on a given node.

  • updateDG() would evaluate the plug for the attribute and cache its value temporarily on the override.
  • updateDevice() would take the temporarily cached value and bind it to a shader stored on the graphic device.
  • endUpdate() would reset the temporary data on the override.

If an MShaderInstance is being used, then the parameters on that instance should be updated during updateDevice().

Note that the override can provide a draw hint as to whether shading will involve semi-transparency. This hint can be provided by overriding the isTransparent() method which gets called between updateDevice() and endUpdate().

3) Drawing Phase : The actual drawing using the shader is performed in the pure virtual draw() method. The callback method should return true if it was able to draw successfully. If false is returned then drawing will be done using the default shader used for unsupported materials.

Drawing is explicitly not intermixed with the data update on purpose. By the time draw is being called all evaluation should have been completed. If there is user data that needs to be passed between the update and drawing phases the override must cache that data itself. It is an error to access the Maya dependency graph during draw and attempts to do so may result in instability.

Although it is possible for implementations of the draw() method to handle all shader setup and geometry draw, the expected use of the draw() method is to do shader setup only. Then drawGeometry() can be called to allow Maya to handle the actual geometry drawing. If manual geometry binding is required however, it is possible to query the hardware resource handles through the geometry on each render item in the render item list passed into the draw() method.

The handlesDraw() method will be invoked at the beginning of the draw phase to allow the plugin to indicate whether it will handle drawing based on the current draw context information. If this method returns false then shaderInstance(), activateKey(), draw() and terminateKey() will not be called. The default implementation will return true if the shader is being used for a color pass and there is no per-frame shader override specified. An example of when such a shader override is used is when all objects in the scene are set to draw with the "default material".

The activateKey() and terminateKey() method will also be invoked in the draw phase each time a render item is drawn with a different shader key (see the discussion above about shader keys). The activateKey() and terminateKey() methods can be used to optimize rendering by configuring the rendering state only once for a batch of draw() calls that are sharing the same shader key.

The sequence of invocations will look like:

  • shaderOverrideA->activateKey(...)
  • shaderOverrideA->draw(...)
  • shaderOverrideB->draw(...)
  • shaderOverrideC->draw(...)
  • ...
  • shaderOverrideA->terminateKey(...)
  • shaderOverrideX->activateKey(...)

Note that the terminateKey() callback is always invoked on the same MPxShaderOverride instance as the one used to invoked the activateKey() callback.

If an MShaderInstance is being used for drawing, in order to take advatange of the batch drawing optimization, the shaderInstance() method should return the shader instance to use. The shader instance should then be bound during activateKey() and unbound during terminateKey(). An MShaderInstance can be bound and unbound from within the draw() method but will not be optimized for batch drawing.

The sequence using an MShaderInstance and the batch optimization will look like:

  • shaderOverrideA->shaderInstance(...)
  • shaderOverrideA->activateKey(...)
  • shaderOverrideA->draw(...)
  • shaderOverrideB->draw(...)
  • shaderOverrideC->draw(...)
  • ...
  • shaderOverrideA->terminateKey(...)
  • shaderOverrideX->shaderInstance(...)
  • shaderOverrideX->activateKey(...)

Note if full draw control is desired, the proxy class MPxDrawOverride may be more appropriate.

Implementations of MPxShaderOverride must be registered with Maya through MDrawRegistry.

Examples:

cgfxShaderNode.cpp, cgfxShaderNode.h, hwColorPerVertexShader.cpp, and hwPhongShader.cpp.

#include <MPxShaderOverride.h>

List of all members.

Classes

struct  MInitContext
 Initialization context used by advanced initalization method. More...
struct  MInitFeedback
 Data to pass back to Maya after initialization. More...

Public Member Functions

 MPxShaderOverride (const MObject &obj)
 Construct an MPxShaderOverride.
virtual ~MPxShaderOverride ()
 Destructor.
virtual MString initialize (MObject shader)
 Initialization occurs when Maya determines that the hardware shader needs to be rebuilt.
virtual MString initialize (const MInitContext &initContext, MInitFeedback &initFeedback)
 Initialization occurs when Maya determines that the hardware shader needs to be rebuilt.
virtual void updateDG (MObject object)
 This is the first part of the update phase.
virtual void updateDevice ()
 This is the second part of the update phase.
virtual void endUpdate ()
 This is the final part of the update phase.
virtual bool handlesDraw (MDrawContext &context)
 This method indicates whether the shader will handle the drawing based on the context passed in.
virtual void activateKey (MDrawContext &context, const MString &key)
 This is the activateKey callback.
virtual bool draw (MDrawContext &context, const MRenderItemList &renderItemList) const =0
 This is the draw callback, the method is called during the draw phase.
virtual void terminateKey (MDrawContext &context, const MString &key)
 This is the terminateKey callback.
virtual MHWRender::DrawAPI supportedDrawAPIs () const
 Returns the draw API supported by this override.
virtual bool isTransparent ()
 During the update phase the override will be called to return whether it will be drawing with semi-transparency.
virtual bool overridesDrawState ()
 During the draw phase this method will be called to determine whether the override will override the draw state when drawing.
virtual bool rebuildAlways ()
 If this method returns true, it will force shader and geometry data to be rebuilt on any change to the shader.
virtual double boundingBoxExtraScale () const
 Override this method to supply an extra scale factor to be applied to the object space bounding box of any objects which use the shader associated with this override.
virtual
MHWRender::MShaderInstance
shaderInstance () const
 Override this method if a shader instance (MShaderInstance) is to be used for drawing.
virtual void activateKey (MDrawContext &context)
 This method is obsolete.
virtual void terminateKey (MDrawContext &context)
 This method is obsolete.

Static Public Member Functions

static const char * className ()
 Returns the name of this class.

Protected Member Functions

MStatus addGeometryRequirement (const MVertexBufferDescriptor &desc)
 During the initialization phase the geometry requirements for the shading effect can be updated.
MStatus addGeometryRequirements (const MVertexBufferDescriptorList &list)
 During the initialization phase the geometry requirements for the shading effect can be updated.
MStatus setGeometryRequirements (const MShaderInstance &shaderInstance)
 During the initialization phase the geometry requirements for the shading effect can be updated.
MStatus addShaderSignature (void *signature, size_t signatureSize)
 During the initialization phase, the "signature" for the shader may be set.
MStatus addShaderSignature (const MShaderInstance &shaderInsance)
 During the initialization phase, the "signature" for the shader may be set.
MStatus addIndexingRequirement (const MIndexBufferDescriptor &desc)
 During the initialization phase the indexing requirements for the shading effect can be updated.
void drawGeometry (const MDrawContext &context) const
 This method may be called from draw() and will cause Maya to immediately draw the current geometry using the current state of the draw API.

Constructor & Destructor Documentation

MPxShaderOverride ( const MObject obj)

Construct an MPxShaderOverride.

Parameters:
[in]objThe Maya shading node this override will be used for

Member Function Documentation

MString initialize ( MObject  shader) [virtual]

Initialization occurs when Maya determines that the hardware shader needs to be rebuilt.

Any initialization work may be performed here. Also, this is the only time that calls to addGeometryRequirement() may occur.

Changes to parameter values on the Maya shading node will not trigger a call to this method by default. If parameter value changes may cause the shader to alter its geometry requirements, rebuildAlways() should be overridden to return true. This will cause initialize() to be called before every update.

The implementation of this method must return the shader key that is used to identify the MPxShaderOverride instances representing the same shader.

Note: There are two versions of the initialize method. Derived classes should override exactly one of them.

The default implementation returns a constant string.

Parameters:
[in]shaderThe Maya shading node this override is used for
Returns:
The shader key
MString initialize ( const MInitContext initContext,
MInitFeedback initFeedback 
) [virtual]

Initialization occurs when Maya determines that the hardware shader needs to be rebuilt.

Any initialization work may be performed here. Also, this is the only time that calls to addGeometryRequirement() may occur.

Changes to parameter values on the Maya shading node will not trigger a call to this method by default. If parameter value changes may cause the shader to alter its geometry requirements, rebuildAlways() should be overridden to return true. This will cause initialize() to be called before every update.

The implementation of this method must return the shader key that is used to identify the MPxShaderOverride instances representing the same shader.

Implementations of this method may also allocate and return custom user data derived from MUserData. This user data will be attached to the MRenderItem representing the association of the shader to the geometry instance specified by the initContext.dagPath parameter. If the deleteAfterUse() method returns true for the MUserData instance then the data object will automatically be deleted when the render item is deleted. Otherwise, the lifetime of the user data object is the responsibility of the caller. This user data may be accessed in the draw phase directly from the render item being drawn. Note that Maya objects which have render items that have custom user data can only be consolidated when the user data pointer on each item references the same user data instance. Draw performance will suffer without consolidation, however if the user data object is shared then memory management must be handled manually (ie. deleteAfterUse() should return false).

Note: There are two versions of the initialize method. Derived classes should override exactly one of them.

The default implementation simply returns the result of MPxShaderOverride::initialize(initContext.shader).

Parameters:
[in]initContextContext information about the current initialization call, such as the path to the Maya object being bound to the shader.
[out]initFeedbackData to return to Maya after initialization such as blind data to attach to the render item.
Returns:
The shader key
void updateDG ( MObject  obj) [virtual]

This is the first part of the update phase.

Perform any work required to update the shading effect which is related to evaluating the dependency graph. This should be the only place that dependency graph evaluation occurs. Data retrieved from Maya may be cached on the override for use in later stages.

Parameters:
[in]objThe Maya shading node this override is used for.
void updateDevice ( ) [virtual]

This is the second part of the update phase.

Perform any work required to update the shading effect which is related to accessing the underlying graphics device. This is the only place that the graphics device may be safely accessed other than at draw time.

void endUpdate ( ) [virtual]

This is the final part of the update phase.

This method is called by Maya to allow the plugin to clean up any data or state from the previous update stages. No dependency graph evaluation, nor graphics device access should be performed during this phase.

bool handlesDraw ( MDrawContext context) [virtual]

This method indicates whether the shader will handle the drawing based on the context passed in.

The default implementation will check the pass context. If the pass semantic is specified to be a color pass and the pass has no shader override (MPassContext::hasShaderOverride() returns false) then this method will return true.

Parameters:
[in]contextThe current draw context
Returns:
true if shader handles drawing
void activateKey ( MDrawContext context,
const MString key 
) [virtual]

This is the activateKey callback.

This method is called during the draw phase before invoking any draw() callback that are sharing a common shader key.

The default implementation is empty.

Parameters:
[in,out]contextThe current draw context
[in]keyThe shader key that is being activated
bool draw ( MDrawContext context,
const MRenderItemList renderItemList 
) const [pure virtual]

This is the draw callback, the method is called during the draw phase.

The expected implementation of this method is to do shader setup and then call drawGeometry() to allow Maya to handle the actual geometry drawing. It is however possible to do all shader setup and geometry draw here directly by accessing the hardware resource handles for geometry and index buffers through the geometry associated with each render item in the render item list.

No dependency graph evaluation should occur during this phase. If data from Maya is needed here it must be cached on this object (or elsewhere) during the update phase.

This method should return true on successful draw. If false is returned, Maya will attempt to draw using the default internal draw mechanism.

Information about the current GPU state may be accessed through the MDrawContext object passed to this method. The MRenderItemList object contains one render item for each object that is meant to be drawn by this method.

Parameters:
[in,out]contextThe current draw context
[in]renderItemListThe list of renderable items to draw
Returns:
True if draw was successful, false otherwise.
void terminateKey ( MDrawContext context,
const MString key 
) [virtual]

This is the terminateKey callback.

This method is called during the draw phase after invoking draw() callbacks that are sharing a common shader key.

The default implementation is empty.

Parameters:
[in,out]contextThe current draw context
[in]keyThe shader key that is being terminated
MHWRender::DrawAPI supportedDrawAPIs ( ) const [virtual]

Returns the draw API supported by this override.

The returned value may be formed as the bitwise 'or' of MHWRender::DrawAPI elements to indicate that the override supports multiple draw APIs. This method returns 'MHWRender::kOpenGL' by default.

Returns:
The draw API supported by this override
bool isTransparent ( ) [virtual]

During the update phase the override will be called to return whether it will be drawing with semi-transparency.

This call occurs after updateDevice() which allows for any device evaluation to occur to determine the transparency state.

The default return value is false.

Returns:
True if semi-transparent drawing should occur.
bool overridesDrawState ( ) [virtual]

During the draw phase this method will be called to determine whether the override will override the draw state when drawing.

This call occurs after updateDevice() which allows for any device evaluation to occur to determine if the override will override the draw state.

The Viewport 2.0 renderer will skip setting the draw state for plugins that will override the draw state when drawing. Note that the MPxShaderOverride::terminateKey() should still return the draw state to the value it had when activateKey was called.

The default return value is false.

Returns:
True if the override overrides the draw state.
bool rebuildAlways ( ) [virtual]

If this method returns true, it will force shader and geometry data to be rebuilt on any change to the shader.

This may be necessary for shaders that request specific named data sets like UVs or CPVs. Any change to the required data set means that geometry needs to be rebuilt.

The default return value is false.

Returns:
True if the shader and geometry should be rebuilt on every update.
double boundingBoxExtraScale ( ) const [virtual]

Override this method to supply an extra scale factor to be applied to the object space bounding box of any objects which use the shader associated with this override.

This is to allow the shader to indicate that the bounding box should be bigger than just the base geometry; normally due to shading effects like displacement. Note that the value returned here will only be used to put a lower bound on the extra scale applied to the bounding box. It may be made larger due to the demands of other shaders associated with the object.

This method will be called any time a change occurs which may affect the bounding box of associated objects. It is acceptable to access the Maya dependency graph within calls to this method as it will never be called during draw.

The default implementation returns the unit scale factor (1.0).

Returns:
Extra scale factor
MHWRender::MShaderInstance * shaderInstance ( ) const [virtual]

Override this method if a shader instance (MShaderInstance) is to be used for drawing.

The default implementation returns a NULL value indicating that no shader instance will be used.

Returns:
Shader instance.
const char * className ( ) [static]

Returns the name of this class.

Returns:
Name of this class.
MStatus addGeometryRequirement ( const MVertexBufferDescriptor desc) [protected]

During the initialization phase the geometry requirements for the shading effect can be updated.

The update is accomplished by calling this method once for each new data stream that needs to be added to the list of requirements.

If the geometry has multiple fields of the same type associated with it (e.g. multiple UV sets) the 'name' attribute of the vertex descriptor can be used to select the desired one. If that member is empty or does not match any of the fields then the default field of that type will be used.

Not all combinations of the 'semantic', 'dataType' and 'dimension' attributes of the vertex descriptor are permitted. Currently

  • position, normal, tangent and bitangent can be 3-float or 4-float,
  • color and tangent with sign vectors are 4-float,
  • texture coordinates are 2-float, or any type if used as custom semantic.
Parameters:
[in]descThe description of the geometry requirement to add
Returns:
Status code
Status Codes:
MStatus addGeometryRequirements ( const MVertexBufferDescriptorList list) [protected]

During the initialization phase the geometry requirements for the shading effect can be updated.

The update is accomplished by calling this method with a list containing descriptions of all data streams that are required by the shader.

If the geometry has multiple fields of the same type associated with it (e.g. multiple UV sets) the 'name' attribute of the vertex descriptor can be used to select the desired one. If that member is empty or does not match any of the fields then the default field of that type will be used.

Not all combinations of the 'semantic', 'dataType' and 'dimension' attributes of the vertex descriptor are permitted. Currently position, normal, tangent and bitangent vectors are 3-float, texture coordinates are 2-float, and colors are 4-float.

This method will attempt to add as many requirements as possible from the list, skipping invalid ones. If kInvalidParameter is returned it means at least one requirement failed to be added.

Parameters:
[in]listThe list of descriptions of the geometry requirements to add
Returns:
Status code
Status Codes:
MStatus setGeometryRequirements ( const MShaderInstance shaderInstance) [protected]

During the initialization phase the geometry requirements for the shading effect can be updated.

The update can be accomplished by calling this method with a shader instance (MShaderInstance). The geometry requirements are copied from the MShaderInstance and used as the current shading effect requirements. If there are any requirements already specified for the shading effect, they will be replaced.

This method should not be used in conjunction with addGeometryRequirement() and addGeometryRequirements() methods. The reason is that when rendering the vertex format used must exactly match the one used for the shader instance. If any additional requirements are added, the geometry may not draw properly.

The corresponding addShaderSignature() method, which takes an MShaderInstance as an input argument, should also be called during initialization if the utility method drawGeometry() is used by the plug-in.

Parameters:
[in]shaderInstanceShader instance
Returns:
Status code
Status Codes:
MStatus addShaderSignature ( void *  signature,
size_t  signatureSize 
) [protected]

During the initialization phase, the "signature" for the shader may be set.

Certain Draw APIs (like DirectX 11) require a signature to allow a shading effect to be properly activated. The signature will be used if the override uses MPxShaderOverride::drawGeometry() in the draw phase in order to perform drawing. If drawing is done manually, adding a shader signature is not necessary.

Parameters:
[in]signatureThe signature of the shader
[in]signatureSizeThe size of the signature block
Returns:
Status code
Status Codes:
MStatus addShaderSignature ( const MShaderInstance shaderInstance) [protected]

During the initialization phase, the "signature" for the shader may be set.

Certain Draw APIs (like DirectX 11) require a signature to allow a shading effect to be properly activated. The signature will be used if the override uses MPxShaderOverride::drawGeometry() in the draw phase in order to perform drawing. If drawing is done manually, adding a shader signature is not necessary.

Parameters:
[in]shaderInstanceAdd the signature associated with the shader instance
Returns:
Status code
Status Codes:
MStatus addIndexingRequirement ( const MIndexBufferDescriptor desc) [protected]

During the initialization phase the indexing requirements for the shading effect can be updated.

The update is accomplished by calling this method once for each new index stream required by the shader.

A shader override can specify the type of primitive it supports. If the shader relies on a special primitive type like kPatch it should use this method to indicate that requirement to the system. Not all fields on the MIndexBufferDescriptor need to be filled in when using this method. Only the name(), primitive(), and primitiveStride() values are important for a shader to report as requirements.

The primitive type must be specified if the shader requires a primitive type different then a standard point, line, or triangle list or strip. The primitiveStride must be specified if using the kPatch primitive type. Valid values range from 1 - 32. The name can be specified to trigger a custom MPxPrimitiveGenerator plugin to be used to produce the desired primitive tessellation. When requesting custom primitives you should register an MPxPrimitiveGenerator that knows how to produce the custom primitive needed by the shader.

Parameters:
[in]descThe description of the indexing requirement to add
Returns:
Status code
Status Codes:
void drawGeometry ( const MDrawContext context) const [protected]

This method may be called from draw() and will cause Maya to immediately draw the current geometry using the current state of the draw API.

Parameters:
[in]contextThe current draw context
Examples:
hwPhongShader.cpp.
void activateKey ( MDrawContext context) [virtual]

This method is obsolete.

Deprecated:
Use the activateKey(MDrawContext&, const MString&) version instead.
Parameters:
[in,out]context
void terminateKey ( MDrawContext context) [virtual]

This method is obsolete.

Deprecated:
Use the terminateKey(MDrawContext&, const MString&) version instead.
Parameters:
[in,out]context

MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride
MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride MPxShaderOverride