0
$\begingroup$

I'm working on a mesh in Blender where I've intentionally merged some vertices in Edit Mode—the geometry is exactly how I want it. However, after merging these vertices, the UV map hasn't updated accordingly. This results in gaps between UV vertices because the merged vertices in the mesh remain unconnected in the UV space.

I can manually detect these gaps and use the Stitch tool (Shift+V) to fix them, but this is time-consuming for a large number of vertices. I'd like to automate this process with a script to speed up my workflow.

My goals are:

Detect in the UV map the vertices that are on the borders of UV islands, shared by two islands, which have been merged in the mesh but remain separate in the UV map. Automatically apply the Stitch operation to these vertices to close the gaps in the UV map. Is it possible to achieve this through Python scripting in Blender? If so, how can I approach writing such a script?

Any guidance, suggestions, or examples would be greatly appreciated!

I tried simple approaches like:

import bpy
import bmesh

def merge_vertices_and_uv_stitch():
    # In Edit mode
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_mode(type="VERT")

    # Get the active mesh
    obj = bpy.context.edit_object
    me = obj.data
    bm = bmesh.from_edit_mesh(me)

    # Merge selected vertices at center
    bpy.ops.mesh.merge(type='CENTER')

    # Update the mesh
    bmesh.update_edit_mesh(me)

    # Apply SHIFT+V (Stitch) operation in UV space
    bpy.ops.uv.stitch()

    # Update the mesh again
    bmesh.update_edit_mesh(me)

# Run the function
merge_vertices_and_uv_stitch()

And even more abstract ideas like this which makes things weird:

import bpy
import bmesh

def merge_vertices_preserve_uvs():
    # In edit mode
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_mode(type="VERT")

    # Get the active mesh
    obj = bpy.context.edit_object
    me = obj.data
    bm = bmesh.from_edit_mesh(me)

    # Ensure UV layers are available
    if not bm.loops.layers.uv:
        return

    uv_layer = bm.loops.layers.uv.verify()

    # Store original UV coordinates
    original_uvs = {}
    for face in bm.faces:
        for loop in face.loops:
            if loop.vert.select:
                original_uvs[loop.vert.index] = loop[uv_layer].uv.copy()

    # Merge selected vertices at center
    bpy.ops.mesh.merge(type='CENTER')

    # Update the mesh
    bmesh.update_edit_mesh(me)

    # Restore original UV coordinates
    for face in bm.faces:
        for loop in face.loops:
            if loop.vert.index in original_uvs:
                loop[uv_layer].uv = original_uvs[loop.vert.index]

    # Final update
    bmesh.update_edit_mesh(me)

# Run the function
merge_vertices_preserve_uvs()

No luck until now :(

Example of the kind of gap I mean: enter image description here

$\endgroup$
1
  • $\begingroup$ Just a note: in Blender 4.2, the shortcut for the Stitch operation in the UV editor is Alt+V, not Shift+V. $\endgroup$
    – Blunder
    Commented Oct 10 at 11:21

1 Answer 1

0
$\begingroup$

You can do the merge of the vertices and UV stitch with bmesh operators.

In your first script, you create a bmesh with bm = bmesh.from_edit_mesh(me) but then you don't use it at all. Instead, you are calling bpy.ops. operators like bpy.ops.mesh.merge. These operators work with a context object that defines the active object, the selected verts, etc. and they rely on specific UI elements.

For example, to call bpy.ops.uv.stitch() the UV editor must be open in the current workspace. If there is no UV editor then you get the infamous context is incorrect error:

RuntimeError: Operator bpy.ops.uv.stitch.poll() failed, context is incorrect

Long story short, use the corresponding bmesh.ops. to work with the bmesh.

Instead of bpy.ops.uv.stitch() you can use bmesh.ops.average_vert_facedata() to stitch the UV verts at the center or bmesh.ops.pointmerge_facedata() to snap them at a given vertex.

After stitching the UVs you can merge the verts with bmesh.ops.pointmerge. For merge_co, you can either use the coordinates of the first or last (second) vertex or calculate the center.

Here is a complete script:

import bpy
import bmesh
import mathutils

def merge_verts_with_UV_verts():

    # Get the active mesh
    me = bpy.context.object.data

    # Get a BMesh representation
    bm = bmesh.from_edit_mesh(me)

    # get selected verts
    selected_verts = [v for v in bm.verts if v.select]

    if len(selected_verts) == 0:
        raise Exception("Error! No verts selected. Select 2 verts to merge.")
        return
    elif len(selected_verts) != 2:
        raise Exception("Error! Need exactly 2 verts to merge.")
        return

    # stitch UV verts as you do in the UV Editor
    bmesh.ops.average_vert_facedata(bm, verts=selected_verts)
    # or stitch with snap to first UV vertex
    # bmesh.ops.pointmerge_facedata(bm, verts=selected_verts, vert_snap=selected_verts[0])

    # merge verts at center as you do in Edit mode
    center_co = (selected_verts[0].co + selected_verts[1].co) / 2
    bmesh.ops.pointmerge(bm, verts=selected_verts, merge_co=center_co)

    # Finish up, write the bmesh back to the mesh
    bmesh.update_edit_mesh(me)
    bm.free()  # free and prevent further access


if __name__ == "__main__":
    merge_verts_with_UV_verts()

You can convert the script into an operator and an add-on. Then it's possible to assign a shortcut and menu entry to it if you like.

Be careful with the bmesh operators. They can mess up your mesh and UV map or even crash Blender if you give them the wrong data or args.

$\endgroup$
1
  • $\begingroup$ Hi, sorry for the delayed response. Thank you for the input. However the shared code doesn't work for the intended goal. Indeed this is part of a larger add-on I'm developing. I considered iterating through VG vertices and applying the stitch function to each, but without a smart filter, it's not practical. A manual approach is required for now. I’ll update if I find a solution for others with the same issue. Again thanks. $\endgroup$
    – Ahab Dev
    Commented Oct 18 at 1:42

You must log in to answer this question.

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