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.
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”.
… 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.)
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:
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
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…
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
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 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”
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
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.
I understand that you want to achieve your goal with less work … That’s absolutely fine.
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.
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… .