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 .
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
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 .
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.
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:
- 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?)
- 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:
- Not āIn Modelā until the user applies them to some entity.
- 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ā¦
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.