Porting Simple Material and Texture Map Plug-ins to Nitrous
 
 
 

Simple Material and Texture Map Display API

3ds Max 2012 provides automatic support for displaying material and texture map plug-ins in a in both the Nitrous viewport mode and in Quicksilver renderings. 3ds Max will create a simple visual representation of these plug-ins with basic colors, diffuse and opacity texture maps which can be rendered quickly, and which will appear the same in both Nitrous viewport mode and in Quicksilver.

The following are the APIs are there to support simple and fast material and texture map display:

Materials and texture maps with complex parameters or requiring richer visual representations in the Nitrous viewport display or in the Quicksilver renderer can be developed using via MetaSL shaders. For more information see the section Advanced Material and Texture Map Display API.

ISimpleMaterial

The ISimpleMaterial interface represents a basic material or texture in the Nitrous viewport. 3ds Max automatically creates an instance of an ISimpleMaterial class for any MtlBase derived plug-in . The ISimpleMaterial is accessible from via MtlBase::GetProperty() using the property ID PROPID_SIMPLE_MATERIAL.

ISimpleMaterial* pISimpleMtl = (ISimpleMaterial*)GetProperty(PROPID_SIMPLE_MATERIAL);

ISimpleMaterial supports basic material and texture color properties such as ambient, diffuse, specular, emissive, and self illumination, and supports a few basic shading and fill modes. The Nitrous viewport will query the ISimpleMaterial properties when it displays MtlBase plug-ins.

ISimpleMaterial supports two texture map channels: diffuse and opacity. The Nitrous viewport display driver will also set these up on an ISimpleMaterial instance, but it is the plug-in’s responsibility to create and manage the lifetime of the texture resource associated with a TexHandle object. The TexHandle can be generated by calling TexHandleMaker::MakeHandle().

ITextureDisplay and DisplayTextureHelper

ITextureDisplay is an interface that allows texture map and material plug-ins to control how their diffuse and opacity textures appear in the Nitrous viewport. This can be useful when a plug-in needs to do custom blending of textures, or when the texture maps have different requirements such as different texture coordinates. Creating and maintaining the handles to these textures is the responsibility of the DisplayTextureHelper interface.

To have its diffuse and opacity texture appear correctly in Nitrous viewport, the plug-in must create them by implementing ISimpleMaterial::SetTexture(). For this, the plug-in needs to derive from the ITextureDisplay interface and use DisplayTextureHelper::MakeHandle() to implement ITextureDisplay::SetupTextures() in that interface.

Impact on plug-ins

In practice, all but the most simple materials and texture map plug-ins will require additional code to provide a correct simple visual representation in the Nitrous viewport. The following scenarios provide more details.

Scenario 1: Plug-in supports the display of at most one texture map

If the material or texture map plug-in supports displaying only one texture map, Nitrous will use that map in the diffuse texture map channel by default. If this default behavior is acceptable, there's no additional code required by the plug-in other than the regular code for supporting the display of the texture map. See Scenario 2 below if the default behavior is not acceptable. Note that in order for the plug-in to be able to override Nitrous' default support, it will need to return TRUE from an overridden implementation of the MtlBase::SupportsMultiMapsInViewport() virtual method even if it has only one map.

The key methods a plug-in should implement for supporting one texture map are outlined below:

class Scenario1 : public Texmap 
{ 
	TexHandle* texHandle;
	// Details elided for brevity ... 
}

// Supports displaying textures
BOOL Scenario1::SupportTexDisplay() {return TRUE;}

// Does not support displaying multiple textures
BOOL Scenario1::SupportsMultiMapsInViewport() {return FALSE;}

// Helper method
void Scenario1::DiscardTexHandle() 
{
	if (texHandle) 
	{	
		texHandle->DeleteThis();
		texHandle = NULL;
	}
}

DWORD_PTR Scenario1::GetActiveTexHandle(TimeValue t, TexHandleMaker& maker) 
{
	if (texHandle) 
	{
		if (texHandleValid.InInterval(t))
         		return texHandle->GetHandle();
      		else 
			DiscardTexHandle();
      	}
	texHandle = maker.MakeHandle(GetVPDisplayDIB(t, maker, texHandleValid));
	return texHandle->GetHandle();
}

The class Scenario1 above uses Texmap::GetVPDisplayDIB() to generate a representation of itself. The returned BITMAPINFO is then used by the TexHandleMaker::MakeHandle() to access the raw bitmap data and generate the texture resource needed by the display system. If the texture map is procedural in nature, like marble, it is important to note that the generated result will not look the same as the one rendered by a production renderer; it is a mere interpretation of the procedural effect. For a more accurate viewport representation of the plug-in, metaSL programmable shaders should be used.

Scenario 2: Plug-in supports displaying multiple texture maps

To display multiple texture maps in the viewport, a plug-in must override MtlBase::SupportsMultiMapsInViewport() to returns TRUE. If it does not, ITextureDisplay::SetupTextures() will not be called.

It is important to note that ITextureDisplay supports only the diffuse and opacity texture maps, although the plug-in may support more. For advanced texture map support in the Nitrous viewport, the metaSL programmable shader language needs to be used.

The key code changes a plug-in should implement for supporting multiple texture maps in the Nitrous viewport mode are outlined below:

Scenario 3: Plug-in's viewport and rendered representation differs

It is common for a material plug-in to change its appearance in the viewport compared to its rendered representation, as it may want to display debugging information or additional visual hints in the viewport. In this case the plug-in will want to override the default representation Nitrous created for it by implementing ITextureDisplay::SetupTextures() as in Scenario 2. In addition to setting up the texture maps, the plug-in can call any of the Set functions in ISimpleMaterial to override the various colors, shade and fill modes Nitrous has set automatically.

Texture resource management

It is important to note that in all of the above scenarios the texture resource used by the Nitrous viewport display system is maintained as a TexHandle instance. The plug-in must create a TexHandle for each texture map that is to be displayed in the viewport, even if Nitrous' default texture map handling is not overwritten by the plug-in (see Scenario 1 above). If changes in the plug-in's parameters render the texture map invalid, then the TexHandle must be deleted by calling TexHandle::DeleteThis() which ensures texture resources that may be used by the GPU are freed. Next time the system requests a TexHandle, or when setting up the textures for a ISimpleMaterial instance, if the TexHandle is invalid, the plug-in must create a new handle by calling TexHandleMaker::MakeHandle().