1
$\begingroup$

I have the following object: Source object

The UV grid on the left hand side is what my script currently does. Here is the code:

    def _process_shell_uv(self, object):
        mesh = object.data
        bm = bmesh.new()
        bm.from_mesh(mesh)
        bm.verts.ensure_lookup_table()
        bm.faces.ensure_lookup_table()
        uv_layer = bm.loops.layers.uv.verify()
        verts = bm.verts

        vert_groups = []

        for vert_idx in self.shell_verts['OuterShell']:
            inc = 0
            verts_in_loop = []
            for idx in range(0, 64):
                verts_in_loop.append(vert_idx + inc)
                inc += len(self.shell_verts['All'])
            
            vert_groups.append(verts_in_loop)
        
        a_y_co = 1
        b_y_co = 1
        c_y_co = 0
        d_y_co = 0
        
        for a, b in zip(vert_groups, vert_groups[1:]):
            faces_to_map = []
            a_x_co = 0
            b_x_co = 1
            c_x_co = 1
            d_x_co = 0 

            for idx in range(len(a)):
                if idx == len(a) -1:
                    x = 0
                    y = 0
                else:
                    x = idx+1
                    y = idx+1
                
                for face in bm.faces[:]:
                    if verts[a[idx]] in face.verts[:]\
                    and verts[b[idx]] in face.verts[:]\
                    and verts[a[x]] in face.verts[:]\
                    and verts[b[y]] in face.verts[:]:              
                        faces_to_map.append(face)
                

            for face in faces_to_map:
                lengths = [edge.calc_length() for edge in face.edges[:]]
                face.loops[0][uv_layer].uv = (a_x_co, a_y_co)
                face.loops[1][uv_layer].uv = (b_x_co, b_y_co)
                face.loops[2][uv_layer].uv = (c_x_co, c_y_co)
                face.loops[3][uv_layer].uv = (d_x_co, d_y_co)

                a_x_co += 1
                b_x_co += 1
                c_x_co += 1
                d_x_co += 1
            
            a_y_co += 1
            b_y_co += 1
            c_y_co += 1
            d_y_co += 1

        mesh.update()
        bm.to_mesh(mesh)
        bm.free()
        return

I am essentially taking each face and making a 1x1 UV mapping for it. Each vertical strip of the object is laid out horizontally, like this:

enter image description here

But now what I want to do is instead of each face being a 1x1 square in the UV map, I want it to be related to the original size of the faces; effectively I want to unwrap the entire thing to a flat plane, keeping all of the relative length differences. But I don't know what to do in my code to fix it.

The desired result is something like this: enter image description here

Where each vertical strip is more or less the same according to the geometry. Here is a closer look:

enter image description here

I'm trying to automate a UV unwrapping process so that all of my cylindrical objects follow this kind of format, and more or lesss give me the same result each time. I will add a texture afterwards and have manual control over the scale in a custom addon I am building.

$\endgroup$
7
  • 1
    $\begingroup$ "But I don't know what to do in my code" - it's hard to help you fixing your code (as opposed to just writing an entire new solution) if you don't paste whole code. For example, I can only guess what is self.shell_verts. In principle, your problem is simple: if the UV map should always consist of X/Y aligned grid, and the width of the cells is already correct, you can calculate the height of each cell as h = face.calc_area() / sum((f.calc_area() for f in column_of_faces)) $\endgroup$ Sep 5, 2021 at 11:18
  • 2
    $\begingroup$ blender.stackexchange.com/questions/185087/… $\endgroup$
    – batFINGER
    Sep 5, 2021 at 13:23
  • $\begingroup$ I'm really sorry but I don't understand how either of those answers are pertinent to UV mapping. $\endgroup$
    – DrewTNBD
    Sep 6, 2021 at 11:45
  • $\begingroup$ A UV map is 2 dimensional, therefore it's a 3D figure flattened into a 2D shape. The Q&A mentioned by batFINGER is a similar process. Keep in mind, it's not the same, hence he didn't mark this question as a duplicate, just pointed to a related material. $\endgroup$ Sep 6, 2021 at 12:04
  • $\begingroup$ I've noticed that if I look at my object from the front and run a Blender sphere projection, then scale it along the Y axis to make it tall enough to cover the full UV height space, then I get what I am looking for. So I guess I'm looking to do that at a low level. I don't understand the math in the posts above nor how to apply it to the UV space. I ran the example code and it worked here for duplicating and flattening out a mesh, but I'm having trouble attaching that to my use case. Sorry, I'm being a n00b! I don't expect the answer on a silver plate. I just don't understand what I've got. $\endgroup$
    – DrewTNBD
    Sep 6, 2021 at 13:39

1 Answer 1

0
$\begingroup$

In the end I have settled for a bpy.ops solution:

bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.uv.cylinder_project(direction='ALIGN_TO_OBJECT',
align='POLAR_ZX',
radius=1.0,
correct_aspect=True,
clip_to_bounds=False,
scale_to_bounds=True)

bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .