How to apply materials to individual brep faces

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.

so far I have followed

https://github.com/mcneel/rhino-developer-samples/commit/9372e6fb1d37e34fa6a1cb4b75663905158d0afd#diff-d8501b287b1c9579a6c2547f3d3e2cc8584ca47063087adbccac6dc66327ef5eR54

My Example :

// 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.

Thank you!
Srujan

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;
}

I need to make sure we have a working C# sample for setting up brep face materials.
RH-85331 Add C# sample code for setting brep face materials

Rhino.Render.RenderMaterial.Id is not the same as Rhino.DocObjects.Material.Id. What is the type of selectedRenderMaterial?

I am using RenderMaterial.

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.

Thanks

Sorry, this might not be possible at the moment using RhinoCommon and RenderMaterials. See RH-85331 for details.