# I am trying to create a Script that does a Smart UV unwrap of seperate connected parts of a mesh

This is what I got so far. Note that I have a counter which I check with 10 while I'm testing, so it doesn't pass through everything, since it takes quite a while.

The reason for doing this is that if I select all and smart unwrap, I get a sub optimal result compared to the result I get when selecting each connected part and smart unwrap those parts separately, and then finally at the end, re-pack everything.

I am using this for light UV maps in UE4

Question 1) Seems the select-linked part doesn't work. What am I missing?

Question 2) Seems nothing gets unwrapped (is probably related to Q1)

def getNextVisibleVertex():
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_all(action="DESELECT")
bpy.context.tool_settings.mesh_select_mode = (True , False , False)
mesh=bmesh.from_edit_mesh(bpy.context.object.data)

if hasattr(mesh.verts, "ensure_lookup_table"):
mesh.verts.ensure_lookup_table()

for vert in mesh.verts:
if (not vert.hide):
vert.select = True

bm = bmesh.from_edit_mesh(bpy.context.object.data)
e.select = True
return True
return False

def execute(self, context):
obj = context.object
data = obj.data
i = 0

found = getNextVisibleVertex()

while (found and i < 10):
bpy.ops.uv.smart_project()
bpy.data.objects[name].select = False
# Hide selection so we don't pickup same 1st vertex next time
bpy.ops.mesh.hide(unselected = False)
i = i + 1

found = getNextVisibleVertex()

return {'FINISHED'}


I finally got something that works, it's slow as molasses, but works.

    def execute(self, context):     # execute() is called by blender when running the operator

while (hasMoreVerts()):
bpy.ops.uv.smart_project()
bpy.ops.mesh.hide(unselected = False)
bpy.ops.object.mode_set( mode = 'OBJECT' )

return {'FINISHED'}         # this lets blender know that the operator finished successfully

def register():
bpy.utils.register_class(LightMapUVUnwrapper)

def unregister():
bpy.utils.unregister_class(LightMapUVUnwrapper)

if __name__ == "__main__":
register()

def hasMoreVerts():
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_all(action="DESELECT")
bpy.context.tool_settings.mesh_select_mode = (True , False , False)
mesh=bmesh.from_edit_mesh(bpy.context.object.data)

if hasattr(mesh.verts, "ensure_lookup_table"):
mesh.verts.ensure_lookup_table()

for vert in mesh.verts:
if (not vert.hide):
vert.select = True
return True

return false


In the meantime i continued myself with my own version and currently have this now:

bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_all(action="DESELECT")
bpy.context.tool_settings.mesh_select_mode = (True , False , False)
mesh=bmesh.from_edit_mesh(bpy.context.object.data)

if hasattr(mesh.verts, "ensure_lookup_table"):
mesh.verts.ensure_lookup_table()

for vert in mesh.verts:
if (not vert.hide):
vert.select = True
bpy.ops.uv.smart_project()
bpy.ops.mesh.hide(unselected = False)


However, this crashes Blender for some reason. If i insert a break at the end of the if (not vert.hide) block, it does what it should for 1st set of verts, and if i keep re-running the script, it also works fine, but that's ofc not funny when i have to do that about 400+ times pr. object. So what am i missing since this won't run?

• If i outcomment bpy.ops.uv.smart_project() then it runs and the result is that everything is eventually hidden (as i would expect), but ofc, nothing is unwrapped. So seems i need to "reset" something for the bpy.ops.uv.smart_project() to work inbetween each iteration? Mar 7 '19 at 18:56

Object mode bmesh script

Result of script run on default Cube, Cylinder and Icosphere joined as one object

• Finds all the separate parts via the method outlined in this answer
• The smart UV project operator appears to naff up an edit mode bmesh, so I went for object mode
• Find the islands and make a list of their vertices indices. Hoping the uv operator doesn't mangle these
• Use the islands to select vertices, and hence faces, write to mesh, toggle into edit mode and run the smart project for each island.

Have made a 3 in U grid of UVs per island.. find the islands again.. possibly could once again use the indices.. and shift the UVs using the island index.

import bpy
import bmesh

from time import time
t = time()

def walk_island(vert):
''' walk all un-tagged linked verts '''
vert.tag = True
yield(vert)
if not e.other_vert(vert).tag]

if v.tag:
continue
yield from walk_island(v)

def get_islands(bm, verts=[]):
def tag(verts, switch):
for v in verts:
v.tag = switch
tag(bm.verts, True)
tag(verts, False)
ret = {"islands" : []}
verts = set(verts)
while verts:
v = verts.pop()
island = set(walk_island(v))
faces = set(f.index for x in island for f in x.link_faces if all(v.tag for v in f.verts) )
ret["islands"].append(list(faces))
tag(island, False) # remove tag = True
verts -= island
return ret

context = bpy.context
ob = context.object
me = ob.data
context.tool_settings.mesh_select_mode = (False, False, True)

bm = bmesh.new()
bm.from_mesh(me)
islands = get_islands(bm, verts=bm.verts)["islands"]
off = [False] * len(me.polygons)

#bm.free()
bm.clear()
for island in islands:
select = off[:]
for i in island:
select[i] = True
me.polygons.foreach_set("select", select)

bpy.ops.object.mode_set(mode='EDIT', toggle=True)

bpy.ops.uv.smart_project()
bpy.ops.object.mode_set(toggle=True )

uv_arrange = False
cols, rows = (10, 10)
if uv_arrange:
# arrange the UV islands

bm.from_mesh(me)

for i, island in enumerate(islands):

# try and move uvs.. prob need to
bm.verts.ensure_lookup_table()
uv_layer = bm.loops.layers.uv.verify()

bm.faces.ensure_lookup_table()

for f in [bm.faces[k] for k in island]:
for l in f.loops:
luv = l[uv_layer]
if luv.uv.x > 1:
luv.uv.x %= 1
luv.uv.x += i % cols
luv.uv.y += i // rows
bm.to_mesh(me)
me.update()

print("Finished in ", time() - t, "secs")


EDIT.

Fixed a couple of things and optimized somewhat for speed.

TESTING

Test Result 10 x 10 grid of UVs distributed

Ran a simple test on an object made by applying array modifiers 10 in x 10 in y to produce a mesh with 100 separate cubes. Tests performed on an old Athlon, hopefully quicker on newer hw.

Finished in  1.238006353378296 secs
Finished in  1.2316277027130127 secs
Finished in  1.2290542125701904 secs


with UV arranging

Finished in  1.2544517517089844 secs
Finished in  1.2524430751800537 secs
Finished in  1.2818937301635742 secs


On a 10 x 10 x 10 (1000) cube mesh. (no arranging)

Finished in  86.3908383846283 secs


@Jonas Mølgaard

Finished in  1.4964582920074463 secs
Finished in  1.4645237922668457 secs
Finished in  1.4904592037200928 secs


On a 10 x 10 x 10 (1000) cube mesh.

Finished in  102.47884035110474 secs


** added bpy.ops.mesh.reveal() call to unhide geometry to test multiple times

EDIT

In the meantime i continued myself with my own version and currently have this now ... However, this crashes Blender for some reason.

as mentioned above the smart uv operator trashes the edit bmesh, after which it becomes invalid.

Re comment:

I tried your script, but it never ends. Either im doing something wrong, or there's some assumption im not aware of.

The proof of concept result of running script is displayed above for a simple mesh made from 3 primitives .

If you are testing on some monster multi part mesh put in the appropriate if i > 10; break The operator prints a message to system console, or put in print statements, so you can see the progress.

You can stop a slow script by pecking at Ctrl C into system console.

• I tried your script, but it never ends. Either im doing something wrong, or there's some assumption im not aware of. Mar 7 '19 at 18:50
• If i outcomment Mar 7 '19 at 18:54
• about the smart uv operator that mess up the bmesh, is there a way to work around it? Mar 8 '19 at 21:49
• Oh and btw, thanks for taking the time to help me out Mar 8 '19 at 21:49