Removing Materials from RenderMaterialTable

Hi Guys,

Essentially what i try to do is import a few files that contain few materials. After import reassign those to fit the current render engine and delete unused ones.

Everything would be ok besides that I end up with material that is “not initialized” - or however I can call it - I get Octane, VRay, Thea materials that are type of Rhino material.

To be more specific let’s consider the following scenario:
-four 3dm files
-each file have 5 materials of different type (exactly the same ones in each file)

When I import all files (without deleting unused materials) rhino lists 5 materials - what is expected.
(NOTE: Importing is handled by RunScript("_-Import " + filename) as there is no other way for doing this using RhCommon)

After that, I reassign material to imported basing on criteria if material belongs to the currently picked renderer in Rhino.

So only one of those materials is actually used.
One, so I want to delete the remaining four - to prevent file bloat and mess in RenderMaterialTable.

I get those without issue doing sets comparisons of what was imported and what is used at the end of the procedure.

Till this moment everything is working as expected. But as soon as I will call RenderMaterialTable.Remove(renderMaterial) it breaks reassigned currently used material.

The thing is that exactly when I’m deleting those something is happening that breaks the material itself boiling it down to RhinoRender Material (Custom exactly).

So my question is if I should know something that happens just after file/material import or anything like that?

For me, it looks like engines are unable to initialize their own data, and deleting other materials causes issue for them interrupting their data recreation due to an unknown reason.

It’d be useful if you attached a simple 3dm file containing one object and the 5 materials.

What is your current query to get the unused materials?

FYI, I only have Rhino Render to work with, so keep that in mind.

Probably. Umm… Without engines you will still get placeholders and if those will break you get Custom instead I need to check that first.

Due to query importedMaterials.Where(rm => !usedMaterials.Contains(rm)); though this tells you nothing :sweat_smile:

In short let’s assume such scenario:

for array of file paths
-runscript import
-make list of imported mats
-add to importedMatsList
-reassign to desired
-check used mat
-add used mat to usedMatsList //or instantly here delete unused mats
//if usedMatsList is used here delete unused mats

Purge is not suitable as its scope extends to whole doc - i’m aiming for ‘local operation’ purge

@nathanletwory is it possible to gain some knowledge when RenderMaterial comparer is launched - I mean when you import 2 files with 5 same materials basicaly you import 10 materials but Rhino does its job and compares materials after import - and deletes it (?) or hides it (?) or i don’t know what is done to those materials but probably im deleting to early materials and one which was picked by rhino while my query lefts different one - wishy washy but could be …

@nathanletwory another question is if I shouldn’t create own comparer though which field/properties should i use for this purpose?

Yup. That was the case RenderMaterial does not have any custom EqualityComparer on RhCommon side so I assumed wrong that comparing RenderMaterial objects will be sufficient while comparing ids of those solved the whole problem.

So for readers in the future don’t forget about your own equality comparers for various RhCommon objects - this tip will save you hours of debugging without finding the real issue :sweat_smile:

You shouldn’t compare object instances directly, on any RhinoCommon type. On different runs for the same material you may end up getting a different instance, even if the underlying data is the same.

The correct way to see if a RenderMaterial (RenderContent) instance is the same is to use the RenderHash property and Name property. Maybe also TypeId, although I think that is not necessary.

The RenderHash determines whether two materials are visually the same. But in theory only one RenderMaterial can have the same name. RenderHash && Name check is the best test, RenderHash && Name && TypeId is even better, but as said I don’t think that is necessary. But wouldn’t hurt.

Yup. That’s what I thought. Thanks for giving a clue for ingredients to use. :+1:

So you’ve now got a working mechanism?

More or less - at least i’m on right track. Now I have a funny case if same material already exists in the document (importing the same set of files second time and reassigning material properly) but seems I found a way to deal with it first getting raw objects then comparing hashes :slight_smile: It’s a great exercise for fancy linq usage :sweat_smile: I’ve marked your answer as a solution as it clearly explains how those should be handled in such cases :wink:

@nathanletwory Checked multiple times now i’m sure checking what i should check and after tripple import of the same file i’ve ended with such case (ofc Show All Materials is picked):

Object have proper material but Materials panel contains two materials that does not exist and cant be edited and whats actually not funny at all that theres no assigned material on the list at all… No crash, no prompts, any ideas? (NOTE: after saving and reopening only one material is present :confused:)

@nathanletwory i totally forgot to ask - what is the default setting for RenderImportConflictOption ?
(Found in Advanced tab in Options)

Anyway, i lost confidence here probably Rhino is sentenced to leave a mess in its material table. In v5 no option to delete material. In v6 and v7 issues when deleting those. FileIO still does not allow to view RenderMaterialTable…

Probably nothing else I can do here. Code looks ok and works ok but UI shows different things. RhinoCommon disallows anyone to import only needed things on own rules so yes that would be it in this matter…

I’m really open to any tips or clues as I totally run out of ideas…

@nathanletwory I don’t know the reason but for each rhino (5,6,7) material import behaves differently - i tried to change names after each import so i will get all materials but that’s not working at all instead of 4 files * 5 materials - i should endup with 20 - depending on rhino version i end up with different results: in v5 its 14, in v6 its 9, in v7 its 12 ??? RenderImportConflictOption actually does not work as it still looses materials…

Actually the worst thing is that even if material name is diffrerent rhino attempts to merge those materials… Could you reveal what exactly is taken into account when creating RenderHash ?

I see that it depends on render engine plugin developer… I’m screwed …

The best answer to this can be given by @johnc and @andy. But in short the RenderHash is supposed to take into account everything visible - colors, textures and their settings. For instance Name isn’t taken into account, or TypeId or Id and the like.

Say you create two plaster materials, give them different names. Now, if you set the color exactly the same for both then you should get the same RenderHash.

@johnc may be especially interested in RenderImportConflictOption not working as you expect it.

But indeed, if RenderHash is overridden by plug-in developer then it is up to that person.

Where does Rhino merge these materials? If they have different names they should end up as two separate materials in the material editor. This does sound a bit like RH-62518 Materials don’t work properly when inserting 3dm as block. @johnc will know better if it is the same or not.

I thought exactly the same but it turns out that it doesn’t. In scenario like this:

for array of file paths
    RunScript("_-Import " + path);

And RenderImportConflict set to add suffix I never get all 20 materials i get some with [imported] suffix while other are merged. Even if i rename after Import all imported materials and add to those:
importedMaterials.ForEach(m => m.Name = m.Name + "(" + (i+1) + ")"); where i is count of paths - at the end where i should get for eg. SomeMaterialPBR (4) such material doesn’t exist and assigned is SomeMaterialPBR (1) - while (2),(3) and (4) do not exist.

With one crucial difference I don’t use Insert.

Again @johnc will know what happens under the hood. I have a feeling it doesn’t matter whether you import or insert. But this is his domain.

Sure. Thanks for your time and all guides. I hope @johnc will kick in here when he will be availible.

I found a way that seems to be valid for v6 but not for v7. Scenario below:

for array of file paths
-runscript import
-make list of imported mats
-rename += (i+1) - where i is current iterator over count of paths
-reassign to desired objects
-check used mat
-delete unused mats
-generate new Guid for used mat (this step, fortunately, breaks CRC check)

v6 seems to fit this weird workaround but v7 brings surprise as below:

And even that i reassigned each material at the end all models that i imported have this legacy v4 material applied…

Nope generating new Id actually breaks material and causes new v4 copy in all rhino versions. So far from my research it seems that changing Name of RenderContent does not fire Rhino.Render.RenderContent.ContentRenamed as if i change Name by hand it seems to work as expected - at least in this part of the whole procedure.


I’ll use the term ‘material’ instead of ‘render content’, but it’s the same for all kinds.

  • By default, the RenderHash depends on the material’s fields. These are parameters such as color, gloss or whatever other visual properties the material has. Name, Type Id, etc. are not including in this because they do not affect the material’s rendered appearance. So, the RenderHash gives the same result for materials that look exactly the same when rendered. Since this also involves child textures, it’s a recursive procedure. But, as mentioned earlier, it is possible for plug-ins to override this procedure and do something different. We have no control over V-Ray or Octane, for example. We only control the default behavior.

  • For the purposes of deciding about merging materials, there is no difference between Import and Insert. Paste is also the same except that it uses different settings controlled by RenderPasteConflictOption. The render hash is only checked when the incoming material has the same instance id as an existing one. Otherwise the material is considered to be totally different even if it looks the same or has the same name. Actually, the merge is one of the rare places where the name of a material matters. Usually, Rhino doesn’t care about material names, but here, if the incoming name already exists, it checks the Render*ConflictOption setting to decide what to do. It tries not to create more materials with the same name (which is the reason for the [imported] postfix).

  • I’m afraid I can’t say if this is the same as RH-62518 because I haven’t looked into that bug yet.