Rename multiple materials from faces/components/groups

Hi i am trying to add a tool to the tt_material_tools-2.7.1, to rename materials from a selection so i can have a nicer output to unreal engine, i have many, many props with many, many materials, the problem i’m facing is nested objects, a group inside a component, a component inside a group, i made a little method, but it says Error: #<SystemStackError: stack level too deep>, clearly i am going into infinty loop or something, any ideas?

Rename Materials.rb (3.3 KB)
Mat Rename Test File.skp (766.2 KB)

practical example of a complex prop:

  • Use no spaces in file names (lower snake_case is Ruby convention). Use spaces around assignments variable_name = expression.
  • If results is nil (user cancelled prompt), you cannot access index 0. Swap the lines around.
  • A face always responds to material and back_material (if that is not the case, some object that is not a face must have slipped in and you better find that bug)
  • Use string interpolation "name_#{expression}" (in double quotes) instead of string concatenation name + (expression).to_s. Then you don’t need to_s and it is more readable.
  • You don’t have to repeat elsif ent.is_a? many times (unless you need to combine it with other criteria). Use case <object> when <class> instead.
  • For nested groups/components, you are restarting the index (each_with_index), so the material name does not consider the indices from the upper levels and clashes with existing indices.
    • Either you need to propagate the upper level indices (list of indices) as parameter in the recursive call and concatenate them "#{@name_mat}-#{indices.join('-')}".
    • Or use a module-wide counter that you increment each time you assign a unique name to a face’s material (there is no concurrency).

rename_materials.rb (3.6 KB)

The infinite recursion that produced the SystemStackError was caused by the following:

  • To access the definition of a component instance (or group), you better use definition. The API documentation is not clear about that parent gives the definition (or model) of the entities collection which contains the receiver. That means when you iterate over the entities of the parent, you get infinite recursion. ent.parent.entities.include?(ent) is true.

I have not exactly understood what naming you have in mind that gives you a nicer structure in Unreal.

  • Multiple faces can have a reference assigned to the same material object. So when you iterate over faces of the same material and change each face’s material’s name, you are overwriting the name of the same material object. This could be a problem? You could just assign the name for the last face (last index)? Or do you want to make the material unique (which is maybe not desirable for a mode with a high polygon count).
  • Since multiple instances can derive from the same component definition, you visit the definition (and contained entities) multiple times and re-assign new names.
    • Either keep a set of visited nodes so that you can prevent visiting nodes multiple times,
    • or make the component instances uniq first (modifies the user’s model) if you want each instance to have unique material names.

Thanks Aerilius!!, the information i got is pure gold, you made me realize the error of checking every face with the same material is a waste of time, also yes the index was not working at all.

The idea here is to have a very complex model with many materials and rename the materials like this: prop_bed_mat_01, prop_bed_mat_02, and so on, keeping instances, when i export to unreal I combine the meshes, meaning i collapse all the faces, components, groups into 1 single object, its easier to handle 1 obj with a million faces than 1 million objects with 1 face, realtime works better with single objects.

Your approach (btw thanks is working not like mine :)), renames the materials per each component inside a group, this is a problem because i might have 1 group with 20 components, props from the web are highly unorganized, now i am getting prop_bed_mat-0-15, prop_bed_mat-1-15, prop_bed_mat-3-15, also i tried it in the bed (practical example), and it freezes sketchup.

Maybe the approach is wrong?, what if we build an array of the mats names of the selection and then simply rename those mats? is it possible in the api to grab materials names and then rename them?

Have you found Model#materials?

model.materials.each_with_index{ |material, index| = "material#{index}" }

Thanks! I have to try it, seems way more simple…

Hi, im back, i realized tomtom had an extension to rename all materials in the scene, its a very similar process to: @Aerilius > model.materials.each_with_index, it grabs all the materials and simply rename them, now i’ve testing @Aerilius (rename_materials.rb) and it works really well, but it freezes in my bed example and now i know the reason, the process checks every entity, ok but i have 135000 faces!, and so many edges, so of course its a never ending process.

Now to the big question: ¿is there a way to choose a component, or a group and get only the mats inside that definition without having to check every entity?

Take a look in the API documentation:

  • You know the exact definition name?
    Look up by name:
  • You know other properties of the definition?
    Iterate and filter:
    Sketchup.active_model.definitions.find_all{ |d|[/^my_definition#\d+/] }
  • You have selected component instances by hand?
    Get definitions of the selection:

Getting all materials of drawing elements within a definition:

materials = definition.entities.grep(Sketchup::Drawingelement).map(&:material).uniq

If you have faces with materials on the backside, you need to concatenate the same for Sketchup::Face and back_material before removing duplicates with uniq.

Thanks @Aerilius, i’ll have to test this as soon as i can, it looks promising…