I am looking to assign materials to individual brep faces using rhinocommon.
The idea is to achieve a PaintBucket style command where I can select N number of subelements (brepFaces) and apply the chosen material to those brepFaces.
// Loop through all selected objects
for (int i = 0; i < gs.ObjectCount; i++)
{
ObjRef objRef = gs.Object(i); // Get each selected object
RhinoObject rhinoObj = objRef.Object(); // Get the RhinoObject
// Apply the material or perform operations on the rhinoObj
if (applyToSubelement && objRef.GeometryComponentIndex.ComponentIndexType == ComponentIndexType.BrepFace)
{
// Handle subelement (BrepFace) material application
var brep = objRef.Brep();
if (brep == null) continue;
// Get the face index from the selected component
int brepFaceIndex = objRef.GeometryComponentIndex.Index;
BrepFace brepFace = brep.Faces[brepFaceIndex];
var parentObjMaterialIndex = rhinoObj.Attributes.MaterialIndex;
var parentObjectMaterial = new Material(doc.Materials[parentObjMaterialIndex]);
// default channel index
int selectedMaterialChannelIndex = -1;
// check if renderMaterial exists in the doc.Materials table or else add the material
var selectedDocMaterial = doc.Materials.FirstOrDefault(docMat => docMat.Id == selectedRenderMaterial.Id);
var selectedDocMaterialIndex = selectedDocMaterial == null ? doc.Materials.Add(selectedRenderMaterial.ToMaterial(RenderTexture.TextureGeneration.Allow)) : selectedDocMaterial.Index;
selectedMaterialChannelIndex = parentObjectMaterial.MaterialChannelIndexFromId(doc.Materials[selectedDocMaterialIndex].Id, true);
var modifiedParentObjectMaterialIndex = doc.Materials.Add(parentObjectMaterial);
// Apply the material to the BrepFace
rhinoObj.Attributes.MaterialIndex = modifiedParentObjectMaterialIndex;
rhinoObj.Attributes.MaterialSource = ObjectMaterialSource.MaterialFromObject; // Use material from parent
// apply the material channel index to the brepface
brepFace.MaterialChannelIndex = selectedMaterialChannelIndex;
// Commit changes
RhinoApp.WriteLine($"Material applied to BrepFace (index: {brepFaceIndex}).");
}
else
{
// Apply material to the entire object
rhinoObj.Attributes.MaterialSource = ObjectMaterialSource.MaterialFromObject;
rhinoObj.Attributes.RenderMaterial = selectedRenderMaterial;
}
rhinoObj.CommitChanges();
}
the objective is to apply to the chosen material to selected subelements only and they may belong to different breps. Also, the material may not exist in Doc.Materials table since the material is chosen from Render.RenderMaterial table.
Would appreciate if someone could give me more clarity on how materials are working for entire objects and brepfaces in general.
Edit to my previous code
this intends to apply material to a brep or a subelement per-click.
keeps going in a loop until user cancels.
protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
var selectedRenderMaterial = SelectMaterialFromLibrary(doc);
if (selectedRenderMaterial == null)
{
RhinoApp.WriteLine("No material selected.");
return Result.Cancel;
}
// Ask the user whether to apply to a subelement or whole object
var gOption = new GetOption();
gOption.SetCommandPrompt("Apply material to subelement or entire object?");
gOption.AddOption("Subelement");
gOption.AddOption("EntireObject");
gOption.AcceptNothing(true);
var result = gOption.Get();
if (result != GetResult.Option)
{
RhinoApp.WriteLine("No option selected, cancelling.");
return Result.Cancel;
}
bool applyToSubelement = gOption.Option().EnglishName == "Subelement";
var keepSelecting = true;
// Event handler for escape key
RhinoApp.EscapeKeyPressed += (sender, args) =>
{
keepSelecting = false; // Stop selection if Escape is pressed
};
while (keepSelecting)
{
// Select the object or subobject
var gObject = new GetObject();
gObject.EnablePreSelect(false, true);
gObject.EnablePostSelect(true);
gObject.SubObjectSelect = applyToSubelement;
var selectionCommandPrompt = applyToSubelement ? "Select subelement(s)" : "Select object(s)";
gObject.SetCommandPrompt(selectionCommandPrompt);
gObject.GeometryFilter = applyToSubelement ? ObjectType.Surface : ObjectType.Brep | ObjectType.Mesh;
gObject.Get();
// Check if the selection was cancelled (e.g., if Escape was pressed)
if (gObject.CommandResult() != Result.Success)
{
keepSelecting = false; // Exit the loop if the selection was cancelled
break;
}
ObjRef objRef = gObject.Object(0); // Get selected object
RhinoObject rhinoObj = objRef.Object(); // Get the RhinoObject
// Apply the material or perform operations on the rhinoObj
if (applyToSubelement && objRef.GeometryComponentIndex.ComponentIndexType == ComponentIndexType.BrepFace)
{
// Handle subelement (BrepFace) material application
var brep = rhinoObj.Geometry as Brep;
// try convert extrusion object to brep object
if (brep == null)
{
var extrusion = rhinoObj.Geometry as Extrusion ;
if(extrusion == null) return Result.Failure;
brep = extrusion.ToBrep();
}
// if brep is still null then return
if (brep == null)
{
return Result.Failure;
}
// Get the face index from the selected component
int brepFaceIndex = objRef.GeometryComponentIndex.Index;
// check if chosen renderMaterial exists in the doc.Materials table or else add the material
var selectedDocMaterial = doc.Materials.FirstOrDefault(docMat => docMat.Id == selectedRenderMaterial.Id);
// adding material to doc.Material table returns its index
var selectedDocMaterialIndex = selectedDocMaterial == null ? doc.Materials.Add(selectedRenderMaterial.ToMaterial(RenderTexture.TextureGeneration.Allow)) : selectedDocMaterial.Index;
// modify parent material with chosen material
var parentObjectMaterialDuplicate = rhinoObj.GetMaterial(true);
var selectedMaterialChannelIndex = parentObjectMaterialDuplicate.MaterialChannelIndexFromId(doc.Materials[selectedDocMaterialIndex].Id, true);
var modifiedParentObjectMaterialIndex = doc.Materials.Add(parentObjectMaterialDuplicate);
// Apply the material to the BrepFace
rhinoObj.Attributes.MaterialIndex = modifiedParentObjectMaterialIndex;
rhinoObj.Attributes.MaterialSource = ObjectMaterialSource.MaterialFromObject; // Use material from parent
// apply the material channel index to the brepface
brep.Faces[brepFaceIndex].MaterialChannelIndex = selectedMaterialChannelIndex;
RhinoApp.WriteLine($"Material applied to BrepFace (index: {brepFaceIndex}).");
}
else
{
// Apply material to the entire object
rhinoObj.Attributes.MaterialSource = ObjectMaterialSource.MaterialFromObject;
rhinoObj.Attributes.RenderMaterial = selectedRenderMaterial;
}
// Commit changes
rhinoObj.CommitChanges();
doc.Views.Redraw();
}
// Unsubscribe the EscapeKeyPressed event after the loop exits
RhinoApp.EscapeKeyPressed -= (sender, args) =>
{
keepSelecting = false; // Cleanup after the escape key event
};
RhinoApp.WriteLine("Material applied to the selected objects/elements.");
return Result.Success;
}
Hi @Jussi_Aaltonen ,
Following up if theres any updates on this.
I have tried the sample code (the one which applies red material to even-indexed brep faces) but havent found a solution yet.