Adding an empty UV map to a mesh

I’m writing an automatic impostor generator. You fit a box, or other mesh object, around the objects to be impostored, run my “Impostor Maker” add on, and you get an impostor. The source objects are rendered in ortho mode looking straight at each face. Those textures are composited into a big image. UVs are constructed to map the texture onto the target object. Intended use is low-LOD models for Second Life, which are usually terrible.

All that works. See screenshot below. On the left, a 3D model with 3163 triangles. On the right, an impostor with 12 triangles. The one thing I can’t do yet is create UVs where none existed. I have to unwrap from the UI first to create the UV arrays so I can overwrite them. Does anyone know how to do that?

Previous discussion in 2016. Result of discussion - nobody found the answer, although many things were tried.

You might be able to use Camera uv projection modifier and then apply it, in the code if you need to do it in python use “bpy.ops.mesh.uv_texture_add()” , unless I misunderstood it :slight_smile:

It turns out to be really easy, but not documented anywhere I can find it.

Add an empty UV layer:

    me =     # mesh info
    if not : # if no UV layer
        me.uv_texture_add()     # add UV layer

The data layer functions don’t seem to be well documented, but usually correspond to the user level “ops” operators. A few others I’ve been able to make work:

Create an image object:

    image =, 
        width=width, height=height, alpha=True) 

Unlike the operator level version, there’s no “color” parameter, so if you need to clear the image to something, that has to be done separately.

Is the data layer documented somewhere? Mostly I’m finding snippets here and on Stack Overflow that hint at how to use it. Yet it’s clearly the way to do things when you need to link up textures, materials, images, nodes, etc.

Oops, it turns out

me.uv_texture_add()     # add UV layer

won’t work. Back to dumb old

    oldactive = = target
    bpy.ops.mesh.uv_texture_add() = oldactive

You almost had it, but you should use to add UV layers instead.

import bpy
C = bpy.context

for o in C.selected_objects:
    if o.type == 'MESH':
        if len( < 1:

The python console in Blender has auto-complete if you need to find your way around

Thanks. That worked.

While I’m on here, any idea how to do a “pack_islands” at the data level? I’ve been looking around in the Python console at things like[‘Cube.001’].uv_textures


but I’m not finding the object on which one can apply “pack_islands”. It would be nice if the data interface was documented.

I don’t think you can do that without the operator, unless you want to write custom packing code. :wink:

‘pack_islands’ operator isn’t well documented, but it works on whatever is the active UV layer on the object. To use it requires the object to be active, to have a UV layer, have the UVs selected, and be in edit mode, though I’m guessing internally it performs transforms on the mesh loops object mode.

If you just want to pass a list of objects or even just an object through the operator, you could wrap it into a function:

import bpy
from bpy import context as C, ops as O, data as D
from time import time

kwargs = { # pack_islands operator settings
'rotate' : True,
'margin' : .001}

def core(obj): = obj

def pack_uvs(input):
    if C.mode != 'OBJECT': #pre-set required mode
    start = time()
    if type(input) is list:
        for obj in input:
            if obj.type == 'MESH':
        end = abs(round(start - time(), 2))
        print ("Finished in", end, "seconds.")

    elif type(input) is bpy.types.Object:
        if input.type == 'MESH':

    for obj in D.objects: # restore selection = False if not obj in selection else True = ao

selection, ao = C.selected_objects, C.active_object


It’s not fast - 10 meshes with 2k islands each will take 4-5 seconds. However 100 meshes with 100 islands only takes 0.25 seconds.
The main take-away is the core() function, as that’s how you can use it on a targeted object.

The alternative is like the post above me, you would need to write your own packing code. Or have some insight into how to override the operator context to work on a given object instead of the active one.

Thanks very much. I was hoping to avoid doing all that, and thanks for writing it out.

Incidentally, setting “margin” to 1 will crash Blender with a segmentation fault. The values for “margin” are in the UV range, so a value of 1 means nothing will fit. Still, crashing is not good.

More on the Blender crash. Traceback:


./blender(BLI_system_backtrace+0x20) [0x1a6c700]
./blender() [0x1078395]
/lib/x86_64-linux-gnu/ [0x7f97f26654b0]
./blender(BKE_mesh_calc_poly_normal+0x37) [0x18075b7]
./blender(RNA_property_float_get_array+0xa4) [0x19073a4]
./blender() [0x146eadf]
./blender(_BaseMathObject_ReadCallback+0x21) [0x14a0461]
./blender() [0x14ab2f7]
./blender(PyObject_Str+0x6f) [0x2e9f92f]
./blender() [0x2ef72be]
./blender() [0x2eb4c33]
./blender(PyObject_Call+0x5c) [0x2e4f06c]
./blender(PyEval_EvalFrameEx+0x3812) [0x2f123a2]
./blender() [0x2f1869e]
./blender(PyEval_EvalFrameEx+0x6c8a) [0x2f1581a]

My add-on, after packing the UVs, is continuing on and precomputing some things before rendering, but it doesn’t get very far after UV packing. It’s crashing on a reference to “poly.normal”, where “poly” is one of the polygons from the object for which we just packed the UVs. That’s apparently a lazy evaluation inside Blender; note the call to “BKE_mesh_calc_poly_normal”,which is something that was called implicitly by trying to access “poly.normal”. This suggests that packing the UVs either invalidated something, and some fields of the object are no longer valid, or it corrupted Blender’s internal representation of the object.

The mesh it’s working on is six faces, a cube, but with the faces not connected. Easy case.

Checking the bug tracker, found two closed bugs regarding UV packing, nothing directly relevant. Any suggestions?

Maybe set margin to 0.03 if 1 is too big?

That wasn’t it. Even margin=0.0 doesn’t fix it. The UV packing actually works OK, and the material and image windows get updated, but some internal data structure may be damaged.

I may have to make a simple example and submit a bug report. But not tonight.

Wrote my own layout engine. Easier than dealing with some bug in pack_islands().

Entire add-on: