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)
linked_verts = [e.other_vert(vert) for e in vert.link_edges
if not e.other_vert(vert).tag]
for v in linked_verts:
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()
verts.add(v)
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.
This Answer.
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
re answer
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.