1
$\begingroup$

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

            # Select linked
            bm = bmesh.from_edit_mesh(bpy.context.object.data)
            for e in vert.link_edges: 
                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'}
$\endgroup$
1
$\begingroup$

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
            bpy.ops.mesh.select_linked()
            return True

    return false
| improve this answer | |
$\endgroup$
0
$\begingroup$

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.mesh.select_linked()
        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?

| improve this answer | |
$\endgroup$
  • $\begingroup$ 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? $\endgroup$ – Jonas Mølgaard Mar 7 '19 at 18:56
  • $\begingroup$ This doesn't really answer the question, and IMO could either be an edit to question or deleted. $\endgroup$ – batFINGER Mar 10 '19 at 9:33
0
$\begingroup$

Object mode bmesh script

enter image description here 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

enter image description here 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.

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.