I'm currently working on a custom viewport in Maya 2018 and 2019. At the moment, I'm implementing my material parser to parse Maya material to my own framework's materials.
However, I'm quite struggling with creating callbacks. I already managed to get the initial callback to work, which can be seen in the code snippet below. This is just a callback to load meshes and materials when they get added to the scene.
MCallbackId material_added_id = MDGMessage::addNodeAddedCallback(
MaterialAddedCallback,
"mesh",
m_material_parser.get(),
&status
);
In this callback function, I get all shaders that are connected to this mesh and try to attach a callback to any changes that are made to the shader object (see code snippet below).
void wmr::MaterialParser::Parse(const MFnMesh& mesh)
{
// ...
MObjectArray shaders; // References to the shaders used on the meshes
MIntArray material_indices; // Indices to the materials in the object array
// Get all attached shaders for this instance
mesh.getConnectedShaders(instance_index, shaders, material_indices);
switch (shaders.length())
{
// No shaders applied to this mesh instance
case 0:
{
}
break;
// All faces use the same material
case 1:
{
MPlug surface_shader = GetSurfaceShader(shaders[0]);
// Invalid surface shader
if (!surface_shader.has_value())
return;
// Find all plugs that are connected to this shader
MPlugArray connected_plugs;
surface_shader.value().connectedTo(connected_plugs, true, false);
// Could not find a valid connected plug
if (connected_plugs.length() != 1)
return;
auto shader_type = GetShaderType(connected_plugs[0].node());
// Shader type not supported by this plug-in
if (shader_type == detail::SurfaceShaderType::UNSUPPORTED)
return;
// Found a Lambert shader
if (shader_type == detail::SurfaceShaderType::LAMBERT)
{
MGlobal::displayInfo("Found a Lambert shader!");
MPlug color_plug = GetPlugByName(connected_plugs[0].node(), "color");
// Retrieve the texture associated with this plug
auto texture_path = GetPlugTexture(color_plug);
// Print the texture location
MGlobal::displayInfo(texture_path.asChar());
// Add callback that filters on material changes
MStatus status;
MCallbackId attributeId = MNodeMessage::addAttributeChangedCallback(
shaders[0],
MaterialCallback,
this,
&status
);
CallbackManager::GetInstance().RegisterCallback(attributeId);
}
}
break;
// Two or more materials are used
default:
{
// ...
break;
}
}
}
void MaterialCallback(MNodeMessage::AttributeMessage msg, MPlug &plug, MPlug &otherPlug, void *clientData)
{
MGlobal::displayInfo("Hey! Im a material callback!");
}
Notice the switch-case. Especially case 1:
, when there is one shader found. "Found Lambert"
is printed in the output when a lambert shader was found. After that, I attach a callback addAttributeChangedCallback
to the shader object. However, this is never triggered.
The output is shown below (after changing the transparency and color value sometimes). As you can see, "Hey! Im a material callback!"
is not printed anywhere, but it should be called when I change an attribute from the material.
// Found a Lambert shader!
//
select -r pCylinder1 ;
setAttr "lambert1.transparency" -type double3 0.0779221 0.0779221 0.0779221 ;
setAttr "lambert1.transparency" -type double3 0.194805 0.194805 0.194805 ;
setAttr "lambert1.color" -type double3 0.0779221 0.0779221 0.0779221 ;
So, I'm not sure what I'm doing wrong here, as there aren't many (or any) samples about this issue as far as I can find.
Some help would be very much appreciated. Thanks in advance!