Adding materials from SketchUp's material collections to the current model

SU comes installed with 17 material collections from Wood to Water.
How do I reference them to add one or more of their materials to the current model?

I just found the work around from a previous post by Dan Rathbun in 2015:

"A workaround is to create a component that has the needed material(s) in it. And load this component definition into the model using DefinitionList#load()3.
Then immediately afterward DefinitionList#purge_unused().

This approach requires you to distribute this material component with your plugin."

Seems 25% of my effort is implementing workarounds.

Haha, know that feel. But now a quite large amount of the time is spent with users who gets their cards rejected by EW instead :stuck_out_tongue: .

Anyhow, the user might not want to purge components. Itā€™s a very unexpected side affect of a plugin. Iā€™m not sure but I think more recent SketchUp versions can delete a single Definition by clearing its Entities.

filename = 'Materials/Brick, Cladding and Siding/Cinder Block.skm'
path = Sketchup.find_support_file(filename)
materials = Sketchup.active_model.materials
material = materials.load(path) if filename

With v2017 will load the SKM from the shipped folders - IF it exists - i.e. provided that the user has not changed the contents of the shipped Materials subfolderā€¦ e.g.

C:\ProgramData\SketchUp\SketchUp 2017\SketchUp\Materials\Brick, Cladding and Siding

1 Like

Are you suggesting that v2017 introduced this new functionality? Or merely that v2017 renamed the folder/file names for the shipped materials?

I need to support v16 and later.

Isnā€™t finding the SketchUp install dir pretty much what Sketchup#find_support_file was for? Now I canā€™t get it to return any useful value though :confused: .

Finding the file is one thing.
Loading it into the model is available with >=v2017ā€¦
With my example code a material is added to the model using the specified SKMā€¦

If you only specify ā€˜Materialsā€™ it looks in the userā€™s defined folder of that name, BUT giving the full path to a potential SKM file succeedsā€¦

The API docs (and TIG) are clear as to the first version availability for each method.

Class: Sketchup::Materials ā€” SketchUp Ruby API Documentation

indicates the first version availability is:

Version:

  • SketchUp 2017

ā€¦

SketchUp 2017 (for the first time) put the shipped file folders in the correct place.
So they are now in the %ProgramData% folder path, rather than the %ProgramFiles% (or %ProgramFiles(x86)% ) folder path.

Youā€™ll need to deal with both folder locations, however TIGā€™s example code ALSO works for v2016:

filename = 'Materials/Brick, Cladding and Siding/Cinder Block.skm'
path = Sketchup.find_support_file(filename)

#=> "C:/Program Files/SketchUp/SketchUp 2016/Materials/Brick, Cladding and Siding/Cinder Block.skm"

If a user has a 32-bit edition of SketchUp 2016 installed on a 64-bit Windows machine (which some users do as they have 64-bit graphics driver issues,) then this above code would also return the correct path, ie:

#=> "C:/Program Files (x86)/SketchUp/SketchUp 2016/Materials/Brick, Cladding and Siding/Cinder Block.skm"

For SketchUp versions before 2017 but with Ruby 2.x and the standard library, you can use the RubyZip library to read the SKM zip archives and re-create a new material using the information in the internal XML files and the texture image file in the archiveā€™s subfolder.

1 Like

It seems that another way to add a material from SketchUpā€™s collections to the ā€œIn Modelā€ collection is to rely on
Sketchup.active_model.materials.current
which (strangely?) returns the currently selected material even if that material is not in the active model.
But the only way to add it to the model for future use by a ruby script is to apply it, at least temporarily to an entity.

This avoids the version problems mentioned above.

Background FYI: My extension adds 6 custom materials with different standard colors to the model. This is easy to do without using any form of ā€œloadā€ method. During model building, the user can execute a command that analyses the entire model and applies the materials to faces and groups depending on orientation and geometric relationships.
No problem so far. Then I decided that I wanted the user to be able to substitute any material from the SketchUp collections for any of the custom materials used for analysis. That led to this thread.

When SketchUp is first loaded with an empty model this method returns nil (which can actually be set nil at anytime by the user if they choose the ā€œdefault materialā€ swatch.)

To have this return the reference to previously loaded models would be a bug IMO.
I just tested this on SU2016 and it returns nil after loading an new model (having been set to a concrete material and painted a group in the previous model. Matters not whether the previous model was saved or unsaved.)

What SketchUp version do you see this behavior in ?

One of the old workarounds was to provide a template that already had a set of materials loaded.

Another was to provide or load a component that had the materials loaded within it. These materials need to be assigned to something like layers (must have at least a cpoint on them) or temporary geometry inside a group, that can be deleted after the load is complete. This leaves the materials behind in the ā€œIn Modelā€ materials collection for later use. Hoping that the user or another plugin does not purge the collection(s).

As evidenced by the workarounds, the community was requesting the ā€œload from fileā€ feature for years until it was finally implemented for version 2017. This took many years for this to happen, I suppose because there were workarounds.


ā€¦ as an aside: The user themselves were always able to drag and drop multiple materials (one by one) from a secondary Materials browser pane into the ā€œIn Modelā€ pane (at least on PC.)

Iā€™m using SU 2017 on PC. My understanding of materials, colors and textures in the API is thin so Iā€™m feeling my way. Iā€™m aware of the possibility that users might ā€œPurge unusedā€ and therefore test and add again my 6 specially named custom materials if necessary. They are all defined with simple color names from the list of colors in the API documentation for the color object. The custom names are referenced by my analysis methods.

Consider this situation: User wants to change the material properties of my ā€œcustom material 1ā€ by selecting a ā€œmaterial_Aā€ in one of SUā€™s material collections outside the model. I code a method to accomplish this that requires him to first make ā€œmaterial_Aā€ the current selected material.

There seem to be 2 ways to code this:

  1. Retrieve the properties of the current material and assign them to ā€œcustom color 1ā€. This fails because the method texture.filename does NOT return the full path as the documentation says it does. Also its seems material.materialType and material.colorize_deltas can be retrieved but not assigned. (Perhaps these are merely derivative of other properties?)
  2. Apply the current material to a face. Reapply the faceā€™s original material to the face. This adds ā€œmaterial_Aā€ to the model. Delete ā€œcustom color 1ā€. Change the name of ā€œmaterial_Aā€ to ā€œcustom material 1ā€. This works. No version dependent load required.

The disadvantage of method 2, is that it does not automatically update all existant references to ā€œcustom color 1ā€ already in the model. So the entire model have to be reanalyzed to apply the new material.

There are material replacer plugins out there that you could get ideas from.

You would likely need to implement this observer callback:

Class: Sketchup::MaterialsObserver ā€” SketchUp Ruby API Documentation

ā€¦ or use a ToolsObserver to detect a change to the PaintTool.

A main reason why I abandon my own SKM importer.

@TIG has a very old SKM tools library over at SketchUcation.

I would not rely upon a bug where references are ā€œleft overā€ from a previous model. This does not occur in SU2016, so it must be a regression for SU2017.

I think you misunderstood me.
When I said ā€œwhich (strangely?) returns the currently selected material even if that material is not in the active model.ā€, I was not implying that the material existed in some previous model.

I was merely referring to the fact that all the materials in the standard material collections that appear whenever you use SketchUp are:

  1. Not ā€œIn Modelā€ until the user applies them to some entity.
  2. and are therefore not referencable by the ruby API via Sketchup.model.materials

The API give us no access to them. ( there is no Sketchup.materials object in the API)

So it is a logical inconsistency that Sketchup.model.materials.current can give us access to a material not yet in the model.

The problem with texture.filename not returning the full path is unrelated to the above. The path is not returned for any material, even those in the model. Perhaps this method is a holdover from before the invention of the skm file.

Sorry, yes I misunderstood.

I now understand what your are getting at. This (what you described as being ā€œstrangeā€,) is normal because a user can select a material to paint with, then decide to do something else instead and cancel the tool by pressing ESC or selecting another tool. So yes, it can return a valid Sketchup::Material object that is not yet a member of the ā€œIn Modelā€ collection until some entity is ā€œpaintedā€ with it.

But it also is ā€œstrangeā€ in that to get itā€™s reference you have to query the modelā€™s materials collection object. On the one hand, youā€™d think this would be an application level property, but they decided to have itā€™s access via the modelā€™s materials collection as that is where people would go looking for it. The possibility also exists that on the Macā€™s multi-model interface, that each model may keep itā€™s own reference to the PaintToolā€™s current material. (Youā€™d need to test this theory.)

On PC, opening a new model will reset the current material to nil.

When they reside on disk, they are mere directories with SKM file objects.

EDIT: I am NOT against having the API implement a nice interface for itā€™s application level collections.

The API gives you access to find the folder paths as @TIG said above.

With that YOU can use the core Ruby Dir class to build a list of material collections and lists.
I recall @TIG posting an informational thread on how to write a Material Browser WebDialog, perhaps over at SketchUcation.

Basic prompting of user for a material:

WIN =( Sketchup.platform == :platform_win rescue RUBY_PLATFORM !~ /darwin/ )
ext = WIN ? 'SketchUp Material|*.skm||;' : '*.skm'
chosen_matl = UI.openpanel("Choose Material",Sketchup.find_support_file('Materials'),ext)
if chosen_matl # nil if user cancels
  # use the material pathname to load a material
end

But again, you need SU2017 to leverage the materials.load() method.

Otherwise youā€™d be doing a component workaround from a special library that would need a temporary component for every darn material in the distribution library.

No, this method only returns local paths for texture images that the user themselves have set using the edit tab of a material.

Think about it, what good would it do (for distributed components) to have a pathname to textures on one of Trimbleā€™s development workstations ? ā€¦ or to a 3DW component authorā€™s local texture files on their computer ?

If YOU want local textures for the materials you can create them manually, or paint a bunch of faces and write a utility the uses that Sketchup::TextureWriter class to export them locally. I donā€™t think you want to publish a bunch of image files like this with your plugin.

for models they do on a macā€¦

and for manually created .skm files unless the author cd into the image directory firstā€¦

I think thatā€™s how the SU ones now avoid the full path, which some older SU ones haveā€¦

the File.basename can still come in handy for getting the original nameā€¦

for v18 ImageRep looks like a better way to make a lot of textures available to extensionsā€¦

@barry_milliken_droid,

if your user opens your material for editing, then selects a replacement texture, it retains your material nameā€¦

is that what your trying to replicate?

or is that just a mac thing?

if not, would it be easier to advise the user that this how to change a textureā€¦

john

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.