Get UV of the texture on a non-painted face inside a group with a texture

Hi Sketchup lover (and ruby dev),

When we apply a texture to a group , the face without material heritate of the group’s texture.
I try to get the UV of the texture on this face (in ruby)?

I read several post (like this How to get the texture of face when just give material to a group/component instance ) but didn’t find any solution

hanks for your answer

This is currently not supported in the APIs. Unpainted faces keeping their UV mapping is a happy accident that so far hasn’t been formalized into a feature.

1 Like

It is possible to work this out. I need to dig up some notes I have, because I keep forgetting the exact details. I think the gist of it is that you use the UVHelper as normal, but adjust them using the width and height of the texture of the material that would be visible. (We have an issue logged to produce an example of how one obtains this.)

2 Likes

I finaly find a simple solution, I apply the material on the face and extract the UV and remove the material
Thanks for your help.

:frowning_with_open_mouth: I’m really not a fan of temporary model changes to do something. Especially when alternatives exists. I’ll dig up some example.

I’d recommend adding a little HACK: comment in your code where you do this, to easier find it and update it should we ever implement API for reading UVs of these faces. It also helps understanding why that material is added for anyone maintaining the code.

EDIT: Missed TT’s comment. If there is a way to read the data without altering the model, that is of course recommended. Changing the model clears the redo stack, can trigger observers in other extensions and probably do other complicated things you don’t really want to have to think about.

Thanks both @ene_su and @tt_su

I finally just add texture but not remove then (because on average size model, it take more than 30 minutes to do so , …)

Of course I will comment it.
If @tt_su find the walk around it will be very helpfull

Here’s a quick mock-up:

module Example
  # @param [Sketchup::Face] face
  # @param [Sketchup::Vertex] vertex
  # @param [Sketchup::InstancePath] instance_path
  # @param [Boolean] front
  # @return [Geom::Point3d] uvq coordinates of the vertex
  def self.vertex_uvq(face, vertex, instance_path, front)
    position = vertex.position
    uv_helper = face.get_UVHelper(front, !front)
    uvq = if front
      uv_helper.get_front_UVQ(position)
    else
      uv_helper.get_back_UVQ(position)
    end
    # The size of instance_path is 1 when the face is at the root of the model.
    # There is then no need to do anything.
    if face_pick.instance_path.size > 1
      path = face_pick.instance_path
      # Find the last element in the instance path to have a textured material.
      element = path.to_a.reverse.find { |element|
        element.material&.texture
      }
      # If we have one we want to adjust the default UVQs by the size of the
      # texture. (Size as in the model space width defined, not pixels).
      if element
        material = element.material
        texture = material.texture
        s = 1.0 / texture.width
        t = 1.0 / texture.height
        # When materials are inherited they are all affine, so Q is always 1.0 and
        # we don't need to take it into account.
        uv_tr = Geom::Transformation.scaling(ORIGIN, s, t, 1.0)
        uvq.transform!(uv_tr)
      end
    end
    uvq
  end
end

The test model had a group with a textured material and the faces of the cube have none:

image

Without adjusting the default UVs: Notice the U value is ~48, not what we want
image

After adjusting the default UV’s based on the texture width and height:
image

Notice that U is now 1.23 - which is what we expect.

(As I’m typing this I forgot to test what happens if the group itself is scaled, might have to account for that somewhere. But I need to run an errand right now. )

UV-test.skp (361.4 KB)

2 Likes

Thanks to @tt_su’s solution , I wrote this. I’m working with mesh. in the loop when I parse all the tree of the file I store the last group or component with texture


 # @group_with_material : the group or instance if a texture
 def main_function(face, group_with_material)
 	mesh = face.mesh(flags) 
 	for i in (1..mesh.count_polygons)
 		polygon = mesh.polygon_at(i)
 		for idx in 0..2
 			point = mesh.point_at(idx)
 			normal = mesh.normal_at(idx)
 			uvw = normal_uvq(mesh, idx, group_with_material)
 		end
        ....
 	end
 end
 
 def vertex_uvq(mesh,idx, group_with_material)
 
 	# get the normal from mesh
 	uvw = mesh.uv_at(idx,true)
 
 	# get the group or the instance 
 	material = group_with_material.material
 	texture = material.texture
 	return uvw unless texture
 	s = 1.0 / texture.width
 	t = 1.0 / texture.height
 	uv_tr = Geom::Transformation.scaling(ORIGIN, s, t, 1.0)
 
 	uvw.transform!(uv_tr)
 
 end
1 Like