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:
ISimpleMaterial
- An interface implemented by 3ds Max that represents a basic material or texture in the Nitrous viewport display system.ITextureDisplay
- An interface implemented by plug-ins that require special processing for proper display of their textures in the viewport.DisplayTextureHelper
- A class implemented by 3ds Max, and derived from class TexHandleMaker
, that is passed toITextureDisplay::SetupTexture()
and used to generate texture handle (TexHandle
) instances as needed.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.
Any material plug-ins can directly get ISimpleMaterial
interface from MtlBase
to customize its display in shaded mode. The following code shows how to get the ISimpleMaterial
interface.
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:
Derive the plug-in from class ITextureDisplay
and make sure its InterfaceServer::GetInterface()
handles the requests for ITEXTURE_DISPLAY_INTERFACE_ID
:
#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
}
The plug-in's override of Mtlbase::SupportsMultiMapsInViewport()
must return TRUE
:
// 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 .