Hi
At the moment I get the Material for a Mesh using GetObjectMaterial(rhinoObject) in RhinoCommon. However if I want to get the per-face material, how is this done please?
Thanks
Paul
Hi
At the moment I get the Material for a Mesh using GetObjectMaterial(rhinoObject) in RhinoCommon. However if I want to get the per-face material, how is this done please?
Thanks
Paul
I think you should get a MeshObject
from the document, then use:
MeshObject mo; // from the document
if (mo.HasSubobjectMaterials)
{
ComponentIndex[] subobjectMaterialComponents = mo.SubobjectMaterialComponents;
foreach(ComponentIndex ci in subobjectMaterialComponents)
{
RenderMaterial subobjectMaterial = mo.GetMaterial(ci);
}
}
Thanks @menno - how do I get the material id for each of the faces please?
Paul
It is subojectMaterial.Id
please consult the API docs Rhino - API References and visual studio intellisense for more info.
Thank you @menno
I am currently iterating though the scene using functions such as the following
Rhino.DocObjects.ObjectEnumeratorSettings settings = new Rhino.DocObjects.ObjectEnumeratorSettings();
//int count = doc.Objects.GetObjectList(settings).c;
settings.IncludeGrips = false;
settings.IncludeLights = true; // false;// true;
settings.ReferenceObjects = true;
var iter = doc.Objects.GetObjectList(settings);
foreach (Rhino.DocObjects.RhinoObject obj in doc.Objects.GetObjectList(settings))
{
bool isMeshable = rhinoObject.IsMeshable(MeshType.Render);
MeshingParameters parameters = rhinoObject.GetRenderMeshParameters();
int numMeshes = rhinoObject.MeshCount(MeshType.Render, parameters);
if (rhinoObject.MeshCount(MeshType.Render, parameters) == 0 && isMeshable)
{
numMeshes = rhinoObject.CreateMeshes(MeshType.Render, parameters, false);
}
// etcc
…so in essence the plugin is deriving the mesh geometry from the scene Meshes rather than a MeshObjects. How do I get the MeshObject for a Mesh please?
Thanks
Paul
Any news here?
This topic is of interest for all Octane users to enable them to assign material on a per face basis.
here is an example:
thanks for looking into this!
@andy - can you help?
FWIW, the changequeue handles this for you by effectively giving you mesh data per material.
Thanks - I am trying to not re-write the entire geometry loader for the Octane plugin in order to support per-face materials. Surely there could have been a RhinoCommon MeshFace.GetMaterialId() function to take care of this?
Paul
Hi - still searching for a solution to this one. The Octane plugin iterates through all the Mesh object in the scene. How can I get a MeshObject from a Mesh please in order to get the per-face materials?
Thanks
Paul
Sorry for the delay. The objects in your test file are not meshes, but Breps, where faces of the Brep are assigned materials individually. As I suspect you know, the BRep has a render mesh, that can be extracted on the face level, so this is a way to connect the subobject material assigned to a Brep face to the rendermesh of that face. Hope this helps!
private string Represent(ComponentIndex ci)
{
return $"ComponentIndex {ci.ComponentIndexType}; Index {ci.Index}";
}
protected override Result RunCommand(RhinoDoc doc, RunMode mode)
{
foreach (var obj in doc.Objects)
{
if (obj.HasSubobjectMaterials)
{
ComponentIndex[] subobjectMaterialComponents = obj.SubobjectMaterialComponents;
foreach (ComponentIndex ci in subobjectMaterialComponents)
{
Material subobjectMaterial = obj.GetMaterial(ci);
RhinoApp.WriteLine(
$"{obj.Geometry.GetType().Name}:{obj.Name} has material {subobjectMaterial.Name} as sub-object material on component {Represent(ci)}");
if (obj is BrepObject bo && ci.ComponentIndexType == ComponentIndexType.BrepFace)
{
Brep b = bo.BrepGeometry;
Mesh subObjectRenderMesh = b.Faces[ci.Index].GetMesh(MeshType.Render);
if (subObjectRenderMesh != null)
{
RhinoApp.WriteLine($"{subObjectRenderMesh.Faces.Count} faces on the render mesh have material {subobjectMaterial.Name} ");
}
}
}
}
}
return Result.Success;
}
Thanks - that is really awesome @menno .
That code is getting the faces that have the per-face material. But then the remaining faces still need to be rendered with the old material - and they do not appear to be available from the above code. So how do you go about determining which faces have already been processed via the above code please?
Thanks
Paul
I’m sorry and please don’t get this the wrong way, but I’m not here to do your work for you, especially not as Octane is a commercial product.
The problem you ask me to solve seems like a fairly trivial thing to do some in-code bookkeeping for.
I always try to keep an open mind, so convince me otherwise if you think I see things wrong.
I understand completely. However in a 100 million polygon scene (and extreme I know), I don’t think it’s reasonable to keep a register of every polygon that has already been provided by SubobjectMaterialComponents. So I am guessing there is a way to assess the polygons that were NOT provided by SubobjectMaterialComponents? I am just trying to make this work
Thanks
Paul
The code I provided was done on a BRep face level, not mesh polygon level. For each BRep face you get the render mesh for which all rendermesh polygons (mesh faces) need to use the same material. It is not possible, as far as I could see, to assign materials to individual mesh faces/polygons.
Thanks @menno
If I have a 6 sided object (cube), with one of the faces as a per-face-material (ie. Brep), and then there is one subobjectMaterialComponents object (being the single face) returned for the object.
Then when I go to render the other 5 faces, I am using meshes = rhinoObject.GetMeshes(MeshType.Render) which is returning 6 Mesh objects - so the per-face-material face ends up getting rendered twice.
How do I determine that a Mesh object from rhinoObject.GetMeshes(MeshType.Render) was one of the Brep meshes that was already rendered? Surely there is a RhinoCommon function/method to do this?
Thanks
Paul
The indices for subobjects that have subobject material assignment correspond with subobject indices.
Also refer to this thread Face materials - #2 by dale
It has useful information related to this.
Thanks so much for your reply @nathanletwory. Your solution (ie. storing the ComponentIndex.Index of the per-face-material Breps, and the ignoring those indexes when processing the rhinoObject.GetSubObjects()) worked!
As a general comment, it feels like this was quite unintuitive to implement, and exposing the HasSubobjectMaterials method at the Mesh Object level would have made things considerably easier.
Again, thank you for the assistance.
Paul
A big thank you also from my side, this is a big help for me and I would suppose also other users, who do use the Rhino/Octane pipeline for architectural visualizations.