Assigning brep per-face materials (C++)

Hello,

I know from the headers opennurbs_brep.h and opennurbs_material.h (and Reading per-face Render Materials) that it’s possible to have individual brep faces use a rendering material that is different from the rendering material used by the parent brep. Though, I have difficulties making it work.

As an example, I want to select a polysurface made of at least three faces and assign a different material to each of them: red, green and blue. Here’s my attempt to do it:

CRhinoCommand::result CCommandSampleFaceMaterial::RunCommand(const CRhinoCommandContext& context)
{
	CRhinoGetObject go;
	go.SetCommandPrompt(L"Select polysurface to attach per face materials");
	go.SetGeometryFilter(CRhinoGetObject::polysrf_object);
	go.EnablePreSelect(TRUE);
	go.EnableSubObjectSelect(FALSE);
	go.GetObjects(1, 1);
	if (go.CommandResult() != CRhinoCommand::success) return go.CommandResult();

	// Create different materials for the faces
	ON_Material matRed, matGreen, matBlue;
	matRed.SetDiffuse(ON_Color::SaturatedRed);
	matGreen.SetDiffuse(ON_Color::SaturatedGreen);
	matBlue.SetDiffuse(ON_Color::SaturatedBlue);

	int matRedIndex = context.m_doc.m_material_table.AddMaterial(matRed);
	int matGreenIndex = context.m_doc.m_material_table.AddMaterial(matGreen);
	int matBlueIndex = context.m_doc.m_material_table.AddMaterial(matBlue);

	// Create parent material
	ON_Material matParent;
	// Add faces materials to parent material channel
	ON_UuidIndex uuidIndexRed, uuidIndexGreen, uuidIndexBlue;
	uuidIndexRed.m_id = matRed.Id(); uuidIndexRed.m_i = matRedIndex;
	uuidIndexGreen.m_id = matRed.Id(); uuidIndexGreen.m_i = matGreenIndex;
	uuidIndexBlue.m_id = matRed.Id(); uuidIndexBlue.m_i = matBlueIndex;
	matParent.m_material_channel.Append(uuidIndexRed);
	matParent.m_material_channel.Append(uuidIndexGreen);
	matParent.m_material_channel.Append(uuidIndexBlue);
	int matParentIndex = context.m_doc.m_material_table.AddMaterial(matParent);

	const CRhinoObjRef& objref = go.Object(0);

	// Get the top level object
	const CRhinoBrepObject* obj = CRhinoBrepObject::Cast(objref.Object());
	if (0 == obj)
		return CRhinoCommand::failure;

	// Duplicate the top level object.
	CRhinoBrepObject* dupe_obj = CRhinoBrepObject::Cast(obj->DuplicateRhinoObject());
	if (!dupe_obj)
		return CRhinoCommand::failure;

	// Get the brep
	ON_Brep* dupe_brep = const_cast<ON_Brep*>(dupe_obj->Brep());
	if (0 == dupe_brep)
		return CRhinoCommand::failure;

	// Assign brep face materials
	for (int indF = 0; indF < dupe_brep->m_F.Count() && indF < 3; ++indF) 
	{
		ON_BrepFace& face = dupe_brep->m_F[indF];
		face.m_face_material_channel = indF + 1;
	}

	// Replace object
	if (!context.m_doc.ReplaceObject(CRhinoObjRef(obj), dupe_obj))
	{
		delete dupe_obj;
		return CRhinoCommand::failure;
	}

	// Assign parent material
	CRhinoObjectAttributes attributes(dupe_obj->Attributes());
	attributes.SetMaterialSource(ON::material_from_object);
	attributes.m_material_index = matParentIndex;

	context.m_doc.ModifyObjectAttributes(CRhinoObjRef(dupe_obj), attributes);
	context.m_doc.Regen();

	return CRhinoCommand::success;
}

Could you take a look to see what is wrong (@dale, I guess)?

Many thanks,
Pablo

Hi @pagarcia,

Here is a sample you can review. Let me know if it helps.

cmdSamplePerFaceMaterial.cpp

– Dale

Hi @dale, thanks!

Though I find an issue: your sample works for almost all materials except for custom materials having bitmap textures. If I have as input a material with a texture like in SampleTexture, the texture is not assigned.
Do you know why this happens?

Pablo

Hi @dale

Do you have any examples in C# that can achieve the same functionality?

Jorin

Hi @JorinChi,

Yes, when I get a change I’ll post something.

– Dale