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)?
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.
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.)
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.
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:
Without adjusting the default UVs: Notice the U value is ~48, not what we want
After adjusting the default UV’s based on the texture width and height:
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. )
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