acoolrocket

Blender - Stitch/sew and move (Bulk multiple UV Islands)

Aug 5th, 2026
53
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.80 KB | None | 0 0
  1. import bpy
  2. import bmesh
  3.  
  4. def _find_image_editor_area(context):
  5. screen = getattr(context, "screen", None)
  6. if not screen:
  7. return None
  8. for area in screen.areas:
  9. if area.type == 'IMAGE_EDITOR':
  10. return area
  11. return None
  12.  
  13. def _is_loop_selected(loop, uv_layer):
  14. # Modern 4.0+ API
  15. if hasattr(loop, "uv_select_vert"):
  16. return loop.uv_select_vert
  17. # Legacy fallback
  18. return getattr(loop[uv_layer], "select", False)
  19.  
  20. def _set_loop_selection(loop, uv_layer, is_selected):
  21. # Modern 4.0+ API
  22. if hasattr(loop, "uv_select_vert_set"):
  23. loop.uv_select_vert_set(is_selected)
  24. if hasattr(loop, "uv_select_edge_set"):
  25. loop.uv_select_edge_set(is_selected)
  26. # Legacy fallback
  27. else:
  28. loop[uv_layer].select = is_selected
  29. if hasattr(loop[uv_layer], "select_edge"):
  30. loop[uv_layer].select_edge = is_selected
  31.  
  32. def _selected_uv_faces(bm, uv_layer):
  33. selected = []
  34. for face in bm.faces:
  35. if any(_is_loop_selected(loop, uv_layer) for loop in face.loops):
  36. selected.append(face)
  37. return selected
  38.  
  39. def _split_selected_faces_into_islands(selected_faces, uv_layer):
  40. islands = []
  41. unvisited = set(selected_faces)
  42.  
  43. while unvisited:
  44. start_face = unvisited.pop()
  45. island = {start_face}
  46. stack = [start_face]
  47.  
  48. while stack:
  49. face = stack.pop()
  50. for edge in face.edges:
  51. for neighbor in edge.link_faces:
  52. if neighbor not in unvisited:
  53. continue
  54. connected = False
  55. for loop_a in face.loops:
  56. if _is_loop_selected(loop_a, uv_layer):
  57. for loop_b in neighbor.loops:
  58. if _is_loop_selected(loop_b, uv_layer) and loop_a.vert == loop_b.vert:
  59. connected = True
  60. break
  61. if connected:
  62. break
  63. if connected:
  64. unvisited.remove(neighbor)
  65. island.add(neighbor)
  66. stack.append(neighbor)
  67. islands.append(list(island))
  68.  
  69. return islands
  70.  
  71.  
  72. def stitch_islands_individually():
  73. obj = bpy.context.active_object
  74. if not obj or obj.type != 'MESH':
  75. print("Error: Must be on a mesh object.")
  76. return
  77. if obj.mode != 'EDIT':
  78. print("Error: Must be in Edit Mode on a mesh object.")
  79. return
  80.  
  81. # 1. Map the islands
  82. me = obj.data
  83. bm = bmesh.from_edit_mesh(me)
  84. uv_layer = bm.loops.layers.uv.verify()
  85.  
  86. selected_faces = _selected_uv_faces(bm, uv_layer)
  87. if not selected_faces:
  88. print("No UV faces selected.")
  89. bm.free()
  90. return
  91.  
  92. islands = _split_selected_faces_into_islands(selected_faces, uv_layer)
  93. print(f"Found {len(islands)} selected UV islands to stitch individually.")
  94.  
  95. # MEMORY FIX: Store indices instead of memory pointers
  96. island_indices = [[f.index for f in island] for island in islands]
  97. original_selection_indices = [f.index for f in selected_faces]
  98.  
  99. # Safely release mesh before running operations
  100. bm.free()
  101.  
  102. uv_area = _find_image_editor_area(bpy.context)
  103. override_kwargs = {"object": obj, "active_object": obj, "edit_object": obj}
  104. if uv_area:
  105. override_kwargs["area"] = uv_area
  106. for region in uv_area.regions:
  107. if region.type == 'WINDOW':
  108. override_kwargs["region"] = region
  109. break
  110. else:
  111. override_kwargs.pop("area", None)
  112.  
  113. # 2. Loop through safe index lists
  114. for index, idx_list in enumerate(island_indices, start=1):
  115. try:
  116. # Open fresh mesh instance
  117. bm = bmesh.from_edit_mesh(me)
  118. bm.faces.ensure_lookup_table()
  119. uv_layer = bm.loops.layers.uv.verify()
  120.  
  121. # Deselect all
  122. for face in bm.faces:
  123. if hasattr(face, "uv_select_set"):
  124. face.uv_select_set(False)
  125. for loop in face.loops:
  126. _set_loop_selection(loop, uv_layer, False)
  127.  
  128. # Select only this island
  129. for i in idx_list:
  130. face = bm.faces[i]
  131. if hasattr(face, "uv_select_set"):
  132. face.uv_select_set(True)
  133. for loop in face.loops:
  134. _set_loop_selection(loop, uv_layer, True)
  135.  
  136. bmesh.update_edit_mesh(me, loop_triangles=False, destructive=False)
  137.  
  138. # Execute Mio3 Stitch on the explicitly selected island
  139. with bpy.context.temp_override(**override_kwargs):
  140. bpy.ops.uv.mio3_stitch(static_island=0, clear_seams=True)
  141.  
  142. except Exception as err:
  143. print(f"Skipped island {index}: {err}")
  144. finally:
  145. # Close mesh instance to prevent crash
  146. bm.free()
  147.  
  148. # 3. Restore original selection
  149. bm = bmesh.from_edit_mesh(me)
  150. bm.faces.ensure_lookup_table()
  151. uv_layer = bm.loops.layers.uv.verify()
  152.  
  153. for face in bm.faces:
  154. if hasattr(face, "uv_select_set"):
  155. face.uv_select_set(False)
  156. for loop in face.loops:
  157. _set_loop_selection(loop, uv_layer, False)
  158.  
  159. for i in original_selection_indices:
  160. face = bm.faces[i]
  161. if hasattr(face, "uv_select_set"):
  162. face.uv_select_set(True)
  163. for loop in face.loops:
  164. _set_loop_selection(loop, uv_layer, True)
  165.  
  166. bmesh.update_edit_mesh(me, loop_triangles=False, destructive=False)
  167. bm.free()
  168.  
  169. print("Individual island stitching complete!")
  170.  
  171. stitch_islands_individually()
Advertisement
Add Comment
Please, Sign In to add comment