Nitrous SDK provides automatic support for displaying material and texture map plug-ins 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 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 HLSL (High Level Shader Language) shaders. For more information see the section Porting Advanced Material and Texture Map Plug-ins to Nitrous.
ISimpleMaterial* pSimpleMaterial = (ISimpleMaterial *)pMtlBase->GetProperty(PROPID_SIMPLE_MATERIAL);
ISimpleMaterial supports basic material and texture color properties such as ambient, diffuse, specular, and emissive, 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 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 the 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.
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 is 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, HLSL shaders must 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 return 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 might support more. For advanced texture map support in the Nitrous viewport, the HLSL 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:
#include <.\Graphics\ITextureDisplay.h> // Material plug-in with multimap support derives from the class ITextureDisplay class Scenario2 : public Mtl, public MaxSDK::Graphics::ITextureDisplay { TexHandle* texHandle1; TexHandle* textHandle2; // Details elided for brevity } BaseInterface* Scenario2::GetInterface(Interface_ID id) { // Handle request for an ITextureDisplay interface if (id == ITEXTURE_DISPLAY_INTERFACE_ID) { return static_cast<ITextureDisplay*>(this); } return Mtl::GetInterface(id); // If Scenario2 is a texture return Texmap::GetInterface }
// Supports displaying textures BOOL Scenario2::SupportTexDisplay() {return TRUE;} // Supports displaying multiple textures // Must return TRUE even if the plug-in has only one map but wants to // override the Nitrous default support for texture maps. BOOL Scenario2::SupportsMultiMapsInViewport() {return TRUE;}
Implement ITextureDisplay::SetupTextures().
This method is called whenever a change to the material or texture is detected by the display system. Call ISimpleMaterial::SetTexture() to specify the actual texture map to be displayed as diffuse and opacity maps. Use the DisplayTextureHelper object derived from class TexHandleMaker to wrap the texture resource into a TexHandle that you can pass to ISimpleMaterial::SetTexture(). DisplayTextureHelper::UpdateTextureMapInfo() needs to be called for the Nitrous graphics driver to extract key data about the texture map to be displayed, such as texture transformations and mapping channels information. This step is important as each texture map can use different UV coordinates based on the mapping channel defined by the plug-in.
void Scenario2::SetupTextures(TimeValue t, DisplayTextureHelper &cb) { ISimpleMaterial *pISimpleMtl = (ISimpleMaterial*) GetProperty(PROPID_SIMPLE_MATERIAL); // Initialize texHandles is needed if (!texHandle1Valid.InInterval(t)) { PBITMAPINFO bmi; // Bitmap creation code elided for brevity - see Scenario 1 // Create the texture resource texHandle1 = cb.MakeHandle(bmi); } if (!texHandle2Valid.InInterval(t)) { PBITMAPINFO bmi; // Bitmap creation code elided for brevity - see Scenario 1 // Create the texture resource texHandle2 = cb.MakeHandle(bmi); } // Clear the textures pISimpleMtl->ClearTextures(); // Setup texture maps for simple display in Nitrous viewport if (texHandle1) { cb.UpdateTextureMapInfo(t, ISimpleMaterial::UsageDiffuse, diffuseMap); pISimpleMtl->SetTexture(texHandle1, ISimpleMaterial::UsageDiffuse); } if (texHandle2) { cb.UpdateTextureMapInfo(t, ISimpleMaterial::UsageOpacity, opacityMap); pISimpleMtl->SetTexture(texHandle2, ISimpleMaterial::UsageOpacity); } }
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 might 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 that 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.
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().
For an example that demonstrates the use of these interfaces, see maxsdk/samples/materials/stdmtl2.cpp.