Trying to run through directory, and re-texture a surface. Eventually want to Render w/ Ruby script

Hey guys,

New to Ruby and Sketchup. But making do with what resources I find. Here’s my .rb file thus far.

[code]# Ruby Script for Retexturing and Rendering Scene from source directory images.

Have top of board game/material selected (Just the top face) before running script.

inputDir = Dir.open “C:/Users/XXXX/Downloads/Notes And Things/Photo_Illustrator_OddandEnds/SketchUp/Gameboard Modeling/TopCovers(Bulk)”

i = 1

inputDir.each do |file|
# Process through Sketchup.
# Get a handle to the selection set.
currentMaterial = Sketchup.active_model.materials.current
currentMaterial.texture = File.absolute_path file
UI.messagebox(file)
# Save Render to outputDir.
view = Sketchup.active_model.active_view
view.write_image “C:/Users/XXX/Downloads/Notes And Things/Photo_Illustrator_OddandEnds/SketchUp/Gameboard Modeling/Renders/BulkOutput/testImage#{i}.png”
i = i + 1
end[/code]

It runs, and produces output write_images. However, the material does not update. I cannot for the life of me figure out why.

Hoping to see these images (left) get placed onto the model, but in the output I only see the original texture (right);

The script requires that you have the material with the texture selected because of the line ‘currentMaterial = Sketchup.active_model.materials.current’ selects that.

In ‘currentMaterial.texture = File.absolute_path file’ I was hoping to find some syntactical error that would lead to nothing being applied, but I’m not seeing anything.

I’m taking screenshots right now with view.write_image just for proof of concept, but hopefully I’ll upgrade that to one of two things;

Create a scene per texture, then Batch Render with Vray after all the textures have been put into a respective scene. OR… Render between retextures. I’m not sure which can work.

This is fragile. You should be assigning material names. Then your code sets the current_material via name.

model = Sketchup.active_model
matls = model.materials
matl = matls["SomeName"]
matls.current= matl unless matl.nil?

Or you get the first face in the selection set.

ss = model.selection
face = ss.grep(Sketchup::Face).first
unless face.nil?
  matl = face.material
end

If the selection is a single_object and it’s a Sketchup::ComponentInstance or Sketchup::Group then you may need to access the object’s definition.entities collection to get a reference to the face.

if ss.single_object?
  obj = ss[0]
  if obj.is_a?(Sketchup::ComponentInstance) || obj.is_a?(Sketchup::Group)
    ents = obj.definition.entities
    face = ents.grep(Sketchup::Face).first
    matl = face.material
  end
end

Usually this is a sync issue. The ruby script is faster than SketchUp is at updating it’s UI or viewport.


Now with regard to getting files. Rather than open a directory object, which needs to be closed, and is problematic if IO errors happen,… just glob the directory for an array of texture file names.

userhome = ENV["USERPROFILE"] || ENV["HOME"]
gameboard = "Downloads/Notes And Things/Photo_Illustrator_OddandEnds/SketchUp/Gameboard Modeling"
imgpath = "#{userhome}/#{gameboard}/TopCovers(Bulk)"

Dir::chdir(imgpath) {
  textures = Dir["TestBoardTop*.png"].sort!
}
# back in previous dir

render = "/Renders/BulkOutput"
outpath = "#{userhome}/#{gameboard}/#{render}"

output = ""
textures.each_with_index do |file,i|
  #
  while i > 0 && !output.empty? && !File.exist?(output)
    Kernel.sleep(0.5)
  end
  output = "#{outpath}/testImage#{i}.png"
  #
  material.texture = "#{imgpath}/#{file}"
  view.refresh
  Kernel.sleep(0.5) # give SketchUp time to refresh the view
  view.write_image( output )
  #
end

Just ideas.

1 Like

I’ll get the ideas implemented and see what progress I can make from there. Thank you for the response!

Like I said, I’m new to Ruby API so I don’t know what’s optimal, but these look like good ideas to me.

There was one inquiry I wanted to make, because it seems that it’s the crux of the script for me. I want to eventually render the same scene where the same face is replaced with the texture from the directory for each render.

Right now, I have the V-Ray plugin, and Render settings/output setup in that.

Is it possible to script that workflow?

Yes, It should be, as I said it likely a sync issue. You have two options, to change the texture of the material gloabally, or change the material of specific faces. The first is likely to be easier, especially if you know the name of the material in the materials collection.

The trick is to wait long enough for SketchUp to update the view, before writing the image, and then to wait again while the image is being processed for the file write (before changing the material’s texture for the next “frame”.)

I just thought of another idea. There is an abstract Animation class that might help in the sync’ing department. Ie, although it’s common, it is not absolutely necessary to change the camera position in a nextFrame() callback method.

Ruby is multi-paradigm. Don’t sweat it.

See my Ruby learning wiki thread:
Ruby Learning Resources [WikiLists]

1 Like

Truly wonderful responses from you Dan. Thank you for giving me some of the answers I needed to get back into the code. I may pop my head back into here in a couple of days when I get some of this stuff implemented.

I was thinking of potentially creating a scene per texture, and save the rendering until last with V-ray’s ‘Batch Render’ option. Which may simplify the sync’ing.

But I’ll look into this Animation option for giving the script pause.