How to deal with plug-in material. (C++)

How to get diffuse color and texture if object uses plug-in material?
Where can I find an example.

Thanks in advance

In most cases, an object’s render material will reside on Rhino’s material table - CRhinoMaterialTable - which exists on the current document.

const CRhinoMaterialTable& material_table = context.m_doc.m_material_table;

To determine an object’s material (index), get the object’s attributes.

const CRhinoObject* object = ....
int material_index = object->Attributes().m_material_index;

Once you have the material index, you can get the material and its properties.

const CRhinoMaterial& material = material_table[material_index];

If your lazy, you can just ask the object for its material.

const CRhinoMaterial& material = object->ObjectMaterial();

The diffuse color can be obtained like this.

ON_Color color = material.m_diffuse;

A material’s bitmap texture can be obtained like this.

int ti = material.FindTexture(0, ON_Texture::bitmap_texture);
if( ti >= 0 )
  ON_wString filename = material.m_textures[ti].m_filename;

With all this said, its possible that some render plug-ins store their material definitions as user data. This user data could be stored on a Rhino material or somewhere else. So depending on the render plug-in you are using, the above may or may not retrieve the data you are looking for…

Thank you.

When:
rhinoObject->Attributes().MaterialSource() == ON::material_from_parent
then
object->Attributes().m_material_index still is valid? Or should I find material id in different way?

From opennurbs_defines.h:

ON::material_from_parent - for objects with parents, like definition geometry in instance references and faces in polysurfaces, this value indicates the material definition should come from the parent. If the object does not have an obvious parent, then treat it the same as ON::material_from_layer.

When you encounter an object whose material source is set to ON::material_from_parent, then in most cases the object is what is called instance definition geometry - geometry that belongs to a block definition.

You can also tell if an object is instance definition geometry by calling CRhinoObject::IsInstanceDefinitionGeometry.

Thus, you look for the owning CRhinoInstanceObject to determine how to handle the material.

Thank you for the detailed explanation. One thing is not clear for me:
If I have CRhinoObject which is CRhinoInstanceObject and get CRhinoInstanceDefinition.
How can I get parent object attributes from CRhinoInstanceDefinition?

Lets say you are iterating through the Rhino document and you encounter a CRhinoInstanceObject. You then obtain the instance geometry from instance definition CRhinoInstanceDefinition::GetObjects. If, while iterating through the instance definition geometry, you find an object whose material is set to ON::material_from_parent, you would then use the material from CRhinoInstanceObject, which is the parent in this “instance.”

Does this help?

Thank you, now I understand.
I work on list of CRhinoObjectMesh after RhinoMeshObjects()
Is that correct approach?

int ColladaExporter::GetMaterialIndex(const CRhinoObjectMesh& rhinoMesh)
{
	int materialIndex = -1;
	int layerIndex = -1;

	switch(rhinoMesh.m_mesh_attributes.MaterialSource())
	{
	case ON::material_from_object:
		materialIndex = rhinoMesh.m_mesh_attributes.m_material_index;
		break;

	case ON::material_from_layer:
		layerIndex = rhinoMesh.m_mesh_attributes.m_layer_index;
		materialIndex = document.m_layer_table[layerIndex].m_material_index;
		break;

	case ON::material_from_parent:
		{
			if(rhinoMesh.m_iref_object)
			{
				materialIndex = rhinoMesh.m_iref_object->Attributes().m_material_index;
			}
		}
		break;
	}

	return materialIndex;
}

Close, but not quite. Something like this is a little better:

int GetMaterialIndex(CRhinoDoc& doc, const CRhinoObjectMesh& object_mesh)
{
  int material_index = -1;
  int layer_index = -1;

  switch (object_mesh.m_mesh_attributes.MaterialSource())
  {
    case ON::material_from_object:
      material_index = object_mesh.m_mesh_attributes.m_material_index;
      break;

    case ON::material_from_layer:
      layer_index = object_mesh.m_mesh_attributes.m_layer_index;
      material_index = doc.m_layer_table[layer_index].m_material_index;
      break;

    case ON::material_from_parent:
    {
      const CRhinoInstanceObject* iref_object = object_mesh.m_iref_object;
      if (iref_object)
      {
        switch (iref_object->Attributes().MaterialSource())
        {
          case ON::material_from_object:
          material_index = iref_object->Attributes().m_material_index;
          break;

          case ON::material_from_layer:
          layer_index = iref_object->Attributes().m_layer_index;
          material_index = doc.m_layer_table[layer_index].m_material_index;
          break;
        }
      }
    }
    break;
  }

  return material_index;
}

The above, though, does not take nested instances into account. A nested instance is a definition that contains a instance reference as instance definition geometry.

For what you are doing, you’d be better off calling RhinoGetRenderMeshes instead of RhinoMeshObjects, as it is designed to get meshes for rendering. You might read its description in rhinoSdkMeshObject.h.

1 Like

Thank you Dale. Your support is great!
I use your tips and use RhinoGetRenderMeshes instead of RhinoMeshObjects.

Finally it seems to be working. Not sure if is optimal.
I post the result cone if someone will have the same issue:

int ColladaExporter::GetMaterialIndex(const CRhinoObjRef& rhinoObjRef)
{
	int materialIndex = -1;
	int layerIndex = -1;

	const CRhinoObject* rhinoObject = rhinoObjRef.Object();
	
	if(!rhinoObject)
		return -1;

	switch(rhinoObject->Attributes().MaterialSource())
	{
	case ON::material_from_object:
		materialIndex = rhinoObject->Attributes().m_material_index;
		break;

	case ON::material_from_layer:
		layerIndex = rhinoObject->Attributes().m_layer_index;
		materialIndex = document.m_layer_table[layerIndex].m_material_index;
		break;

	case ON::material_from_parent:
		{
			const CRhinoObject* parentObject = FindParentObject(rhinoObject);
			if(parentObject)
			{
				switch (parentObject->Attributes().MaterialSource())
				{
				case ON::material_from_object:
					materialIndex = parentObject->Attributes().m_material_index;
					break;

				case ON::material_from_layer:
					layerIndex = parentObject->Attributes().m_layer_index;
					materialIndex = document.m_layer_table[layerIndex].m_material_index;
					break;
				}
			}
		}
		break;
	}

	return materialIndex;
}


const CRhinoObject* ColladaExporter::FindParentObject(const CRhinoObject* rhinoObject)
{
	for(int objectNo=0; objectNo < objects.Count(); ++objectNo)
	{
		const CRhinoInstanceObject* instance = CRhinoInstanceObject::Cast( objects[objectNo] );
		if(instance)
		{
			const CRhinoInstanceDefinition* definition = instance->InstanceDefinition();
			if(definition)
			{
				for(int subObjectNo=0; subObjectNo<definition->ObjectCount(); ++subObjectNo)
				{
					if(definition->Object(subObjectNo) == rhinoObject)
						return objects[objectNo];
				}
			}
		}
	}

	return NULL;
}

Looks good to me.

Here is another version you might consider:

https://github.com/mcneel/Rhino5Samples_CPP/blob/master/SampleCommands/cmdSampleExportRenderMeshes.cpp

Note, if your plug-in was a rendering plug-in, then this is basically what you’d get when you went to render a scene…

Thank you!

I notice your code contains this class: ColladaExporter. Rhino can export Collada .dae files (already). You know this, right?