How to add all SU materials in our list?

Dear friends,
As you know we can have a materials name list by using the following codes.

my_mat_names = []
Sketchup.active_model.materials.each do |mt|
  my_mat_names << mt.name
end
mat_list = my_mat_names.join('|')
prompts = ["Material"]
defaults = ["white"]
list = [mat_list]
title = "Material"
choice = UI.inputbox( prompts, defaults, list, title )

It is the list of materials that we used in our model, not complete SU materials. Of course, by using the following code we can add materials from SU.

my_mat = nil
filename = 'Materials/.../Mat_Name.skm'
path = Sketchup.find_support_file(filename)
materials = Sketchup.active_model.materials
my_mat = materials.load(path)

Adding materials one by one is so difficult also different SU’s have different materials.
1- How can we add all of the materials in SU to our list?
2- How can we add a special material in our plugin and will be available in all SU’s?
Your help will be highly appreciated.

You will have to use the Ruby core Dir class to walk all the material folders.

However, the names as displayed in SketchUp’s material browser may not be exactly the same as the SKM filenames.

1 Like

Can you give me more information?

Dir.chdir("C:/ProgramData/SketchUp/SketchUp 2021/SketchUp")
Dir.each_child("Materials") {|mt_dir|  
  Dir.each_child(Sketchup.find_support_file "Materials/"+ mt_dir) {|mt| 
    puts "Got #{mt}" 
  }
}

We can have the list of directories also we can find all materials but I have to use material directory address completely and it depends on SU version. If I use (Sketchup.find_support_file “Materials”, “”) directory address will be “C:/Users/Majid PC/AppData/Roaming/SketchUp/SketchUp 2021/SketchUp/Materials”.

Instead of …

Dir.chdir("C:/ProgramData/SketchUp/SketchUp 2021/SketchUp")

… use the block form of #chdir so that after the block, the previous working directory is automatically restored.

Here is an example:

NOTE: The #each_child loops are not needed because Dir::glob can recursively “walk” the subdirectories if ** is used as a pattern prefix.

module AuthorNamspace
  module SomeExtension

    extend self

    WIN = Sketchup.platform == :platform_win
    MAC = !WIN

    MATL_FILENAMES = []

    USER_MATL_DIR = Sketchup.find_support_file("Materials")
    USER_DIR = File.dirname(USER_MATL_DIR)

    if WIN
      PROG_MATL_DIR = USER_MATL_DIR.gsub(
        ENV["APPDATA"].gsub(/\\\\|\\/,'/'),
        ENV["PROGRAMDATA"].gsub(/\\\\|\\/,'/')
      )
    else # Mac uses the user path
      PROG_MATL_DIR = USER_MATL_DIR.dup
    end

    def get_material_filenames
      Dir.chdir(PROG_MATL_DIR) do
        # Recursively list all SKM files in subdirectories:
        MATL_FILENAMES << Dir.glob("**/*.{skm,SKM}")
      end
      if WIN
        Dir.chdir(USER_MATL_DIR) do
          # Recursively list all SKM files in subdirectories:
          MATL_FILENAMES << Dir.glob("**/*.{skm,SKM}")
        end
      end
      MATL_FILENAMES.flatten!
    end

  end
end

Referring to the example above, your plugin must create a plugin directory in the user materials folder path. In the example … USER_MATL_DIR.

Then you copy SKM files from your plugin’s directory to the new materials subdirectory. (Ie, your RBZ will have a “matls” distribution subfolder beneath the extension’s folder.)

The user may need to restart SketchUp for the new materials to be listed in the Materials inspector. (Or your extension can try to call the UI.refresh_inspectors method.)

1 Like

Thank you for the codes and information. When I run the upper code, I received the following warning…

:72: warning: already initialized constant MajidMahmoudi::MajInterior::MajInterior::WIN :72: warning: previous definition of WIN was here

is it normal?

As an alternative idea (workaround):
You can create a e.g. maj_mat.skp file with your special materials in it. You can include it in your extension folder beside of your main .rb file, then load it to the model then remove something like:

model = Sketchup.active_model
path = File.join(File.dirname(__FILE__), "maj_mat.skp")
comp = model.definitions.load( path )
model.definitions.remove(comp)

Note: The last code line with: DefinitionList#remove instance_method introduced in SU2018, therefore you have to use other method to purge the “unnecessary” component def in earlier version of SU e.g.

model.definitions.remove(comp) if Sketchup.version.to_i >= 18
#Before SU2018 #remove(definition) does not exist, use purge_unused instead
#Be careful, because it could/will remove unused what created by 'others' too...
model.definitions.purge_unused if Sketchup.version.to_i < 18
2 Likes

More precisely: When I load the upper code in a second time, I received the following warning…

Normally a ruby file of the extension loaded once when SU starts, and the constants are defined only once. During testing, when you are reloading a file the constants will be defined again, that is the reason you get the warning.

A constant is a type of variable which always starts with a capital letter . Constants are used for values that aren’t supposed to change, but Ruby doesn’t prevent you from changing them, but you will see this warning message.
You can find more for example here.Everything You Need to Know About Ruby Constants

During testing, let say Yes it is. But you have to be care to not define the constants e.g. in your methods again. Leave your constants definitions on top of the code outside of the methods…

You can check this topic:

1 Like

Dear Dan,
Following codes load all materials for me. I wish to know if it works for different SU versions and different OS also. Can you confirm it?

win = Sketchup.platform == :platform_win
mt_dir = Sketchup.find_support_file("Materials")
if win
  prog_mt_dir = mt_dir.gsub(
    ENV["APPDATA"].gsub(/\\\\|\\/,'/'),
    ENV["PROGRAMDATA"].gsub(/\\\\|\\/,'/')
  )
else
  prog_mt_dir = mt_dir.dup
end
materials = Sketchup.active_model.materials
Dir.chdir(prog_mt_dir) do
  Dir.glob("**/*.{skm,SKM}") do |mt|
    materials.load(Sketchup.find_support_file "Materials/" + mt)
  end
end
if win
  Dir.chdir(mt_dir) do
    Dir.glob("**/*.{skm,SKM}") do |mt|
      materials.load(Sketchup.find_support_file "Materials/" + mt)
    end
  end
end     

Thank you so much Dezmo. After long time I used constant variables and it is why I confused.

In your snippet you have defined mt_dir, but then do not use it later when you do …

    materials.load(Sketchup.find_support_file "Materials/" + mt)

Why not use this …

    materials.load(File.join(mt_dir, mt))

?

No. I do not have a Mac. And you will need to do your own testing.


All this aside …

WHO in the whole WORLD would want to BLOAT their model file with HUNDREDS of materials ?

The answer is NO ONE.

2 Likes

You may find this useful:
https://sketchucation.com/plugin/2365-material_browser

I did not checked too deeply but, probably e.g. the skm.rb in it, worth to see… :wink:

1 Like

Dear Friends, I load material by using the following code.

        win = Sketchup.platform == :platform_win
        mt_dir = Sketchup.find_support_file("Materials")
        prog_mt_dir = nil
        if win
          prog_mt_dir = mt_dir.gsub(
            ENV["APPDATA"].gsub(/\\\\|\\/,'/'),
            ENV["PROGRAMDATA"].gsub(/\\\\|\\/,'/')
          )
        else
          prog_mt_dir = mt_dir.dup
        end
        materials = Sketchup.active_model.materials
        @w_mat = materials.load(File.join(prog_mt_dir,
        "/Asphalt and Concrete/Blacktop Old 01.skm"))

Code works well in Windows OS but I receive Run Time error in MAC OS. Would you please help me for it?
mt_dir = “/Users/majidmahmoudi/Library/Application Support/SketchUp 2018/SketchUp/Materials”
Error: #<RuntimeError: failed to read material file>

I do not have a Mac too… but waht will happen if you change this:

…to this:

        @w_mat = materials.load(File.join(prog_mt_dir,
        "Asphalt and Concrete", "Blacktop Old 01.skm"))

I check it. No different, Same error. Also prog_mt_dir = “/Users/majidmahmoudi/Library/Application Support/SketchUp 2018/SketchUp/Materials”
Also in windows prog_mt_dir = “C:/ProgramData/SketchUp/SketchUp 2021/SketchUp/Materials”

Have you verified that "Blacktop Old 01.skm" actually exists ?

File.exist?(File.join(prog_mt_dir, "Asphalt and Concrete", "Blacktop Old 01.skm"))

(It does exist on my SU2018 installation, BTW.)

You can start removing files and directories to see if the path is valid …

File.exist?(File.join(prog_mt_dir, "Asphalt and Concrete"))

…

File.exist?(prog_mt_dir)

… etc.

Dear Dan,
Following codes work in MAC OS.

          filename = 'Materials/Asphalt and Concrete/Blacktop Old 01.skm'
          path = Sketchup.find_support_file(filename)
          materials = Sketchup.active_model.materials
          @w_mat = materials.load(path)

Thanks for your attention.

Mr. Dan,
I believe Material a big weak point in Sketchup. A user sees some materials in the Sketchup material list selects one of them and uses it. The user doesn’t care its OS is MAC or Windows. Also user don’t need to be worry about he used it before or not. Why it is not in the same way for ruby? It is not impossible. Even it is not difficult. When I write “Sketchup.active_model.materials” Sketchup can returns materials that show in the user material list (from HD) and when I select a material, they can bring it to memory. (Exactly same as what happens for the user).

It is same.
The Material browser what the user use is built in Tool. It has it own nice browser window for the materials on disk and materials in model etc… When you click on a material in this browser or a Paint Bucket icon on a toolbar this built in tool will be activated.Then this tool will load a material from disk to the model Materials list - unless it is already there - and apply to face, to group…etc.

If you want to do same you have to “mimic” this tool. All methods are exist in Ruby API to do so, I guess :wink:

Also, my mother didn’t tell me anything in English…
When a user wants to use a material, he goes to the “Materials” menu and simply selects one. He doesn’t need to be worry if he used the material before or not or were in the hard disk can find this material or what is the OS.
Now let’s do the same in ruby…
If we used this material before we can find it in the material list.
If we didn’t use it before, we cannot find it in the material list and we should load it. For loading, we need to know the material address and OS…
I am sure for the Sketchup team it was possible to show all materials in the list and when we need automatically load it as they do for the users.

Okay, two foreign speakers …:blush:

I understand that you want to achieve your goal with less work … That’s absolutely fine. :slight_smile:
But:


When the user open New file in Sketchup there is no material loaded.

Which is part of the built in Tool.

Because the built in Tool will take care about it.

Yes, and we, developers know it too. We were not born that we knew, but we learned or examined and figured out.

Yes. They are developed a Tool and built in to SU. This is a Paint bucket (or Material) tool.

They gave you a Ruby API, with all the methods you need to get similar or even better results. Then you can give it to your users. This is the way to do.

Theoretically, they (SU team) can add an “easier methods” to the API, but currently we have to use what we have. :innocent:

I gave you an link above about Material Browser. It is open-source, therefore you can see and study the code and load the material files as you wish… .