Accessing UV data in Python script?

In Blender 2.63, with the addition of BMesh, it looks like UV data got obfuscated, and I can’t find where the data got moved to or how to analyze it.

It used to be (2.62) you could go

me = bpy.data.meshes['MyMesh']
uv = me.uv_textures['UVMap']
for j,face in enumerate(me.faces):
  face_uv = uv.data[j]
  print(face.uv1)
  print(face.uv2)
  print(face.uv3)

back when all faces had three (or four) points.

Now in 2.62, that “face_uv” object you get is a MeshTexturePoly, and it doesn’t have “uvN” properties, it only seems to have an “image” property. You can still set UV coordinates for meshes in 2.63; so where are they actually being saved?

Maybe this will help?

Not sure if my last post is still awaiting moderation or if it was removed because it was an external link but how about like this?

uvs = []
for uv_layer in mesh.uv_layers:
    for x in range(len(uv_layer.data)):
        uvs.append(uv_layer.data[x].uv)

It does look like the data is there under “uv_layers” rather than “uv_textures” in 2.63, though there’s a few bits of strangeness there:

Starting with a default scene, I UVunwrapped the default cube and then do this in the console:

>>> bpy.data.meshes['Cube'].uv_layers.active
bpy.data.meshes['Cube'].uv_layers["UVMap"]

>>> bpy.data.meshes['Cube'].uv_layers["UVMap"]
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
KeyError: 'bpy_prop_collection[key]: key "UVMap" not found'

>>> bpy.data.meshes['Cube'].uv_layers.data["UVMap"]
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
KeyError: 'bpy_struct[key]: key "UVMap" not found'

The element that the console says is returned by the call for the “active” uv_layer can’t actually be called!

The “uv_layers” (bpy_prop_collection) “data” element is actually a reference back to the Mesh type (bpy_types.Mesh), not the collection of UV layers, which appears to make it a broken bpy_prop_collection where you can’t get items by key…?

However, I can grab the return of “active” and it appears to be valid:


>>> uv = bpy.data.meshes['Cube'].uv_layers.active
>>> uv.__class__
<class 'bpy.types.[MeshUVLoopLayer](http://www.blender.org/documentation/blender_python_api_2_63_2/bpy.types.MeshUVLoopLayer.html?highlight=meshuvlooplayer#bpy.types.MeshUVLoopLayer)'>

>>> uv.data[0].uv
Vector((7.923018330302511e-08, 0.33435294032096863))

Ah, of course there’s already documentation on this, categorized as a “gotcha” (makes sense, in my situation): http://www.blender.org/documentation/blender_python_api_2_62_4/info_gotcha.html#upgrading-importers-from-2-62

The new parameters to use is “tessface_uv_textures” for getting at the raw UV triangles; and you have to be in object mode, not edit mode!

ah thanks for the info! i had tried accessing tessface_uv_textures but i couldn’t access it i guess i must have been in edit mode.

tessface cache needs to be updated before using it:

Mesh.update(calc_tessface=True)

uv_layers isn’t available in edit mode. In object mode, you can get the uv coordinates per face loop (from n-gons, no tesselation involved):

for uv_loop in bpy.context.object.data.uv_layers.active.data:
    uv_loop.uv

Default cube has 24 loops, thus also 24 uv coordinates in uv_layers.


the first face ha x vertices, so x uv.co
the second have y vertices, so the uv of the second face are betwen x and x+y
face [(3v),(4v),(7v)]
uv[1,1,1,2,2,2,2,3,3,3,3,3,3,3]
so with 2 loop for you kan easiely have the result

Anybody found a simple sample how to create mesh and assign uv to it from python. I’ve already lost two days for this x(

Here’s my little proof of concept script that shows you how the UV, loops, and vertices are linked in Blender 2.62 and higher:


import bpy

if bpy.app.version[0] < 2 or bpy.app.version[1] < 62:
    raise Exception("Only for Blender 2.62 and above")

bpy.ops.object.mode_set(mode='OBJECT') # Can't access coordinate data in edit mode currently 
me = bpy.data.meshes['Cube']

for f in me.polygons:
    print("Polygon", f.index, "from loop index", f.loop_start, "and length", f.loop_total)
    for i in f.loop_indices: # <-- python Range object with the proper indices already set
        l = me.loops[i] # The loop entry this polygon point refers to
        v = me.vertices[l.vertex_index] # The vertex data that loop entry refers to
        print("	Loop index", l.index, "points to vertex index", l.vertex_index, \ 
            "at position", v.co)
        for j,ul in enumerate(me.uv_layers):
            print("		UV Map", j, "has coordinates", ul.data[l.index].uv, \
                "for this loop index")

The me.loops object is really a lookup list, and each entry is NOT a loop in and of itself. The “loops” are really polygons and are stored in the me.polygons structure. Using me.polygons, you can slice the me.loops list into chunks.

Running this on the default cube shows that me.polygons has six entries (the six faces of the cube). Each polygon refers to four loop indexes, making the me.loops list 24 entries long.

Every UV layer in me.uv_layers has a “data” attribute that is a list that is the same length as the me.loops list. Every me.loops entry relates to the same index entry in the me.uv_layers[n].data list.

GAH! But the me.uv_layers attribute is readonly! I’m trying to port a mesh importer to 2.63, and the mesh data being imported has UV data; how do I create those coordinates…?

i was having issues with the UV data as well but I got it figured out finally!


import bmesh

me = bmesh.new()

me.from_mesh(object.data) #Your mesh data

for face in me.faces:
	uvs = []
	for f in face.loops:
		uv = f[uv_layer].uv
		uvs.extend(uv)
		try:
			uv1x = uvs[0]
			uv1y = 1.0 - uvs[1]
		except:
			uv1x = 0.0
			uv1y = 0.0
		try:
			uv2x = uvs[2]
			uv2y = 1.0 - uvs[3]
		except:
			uv2x = 0.0
			uv2y = 0.0	
		try:
			uv3x = uvs[4]
			uv3y = 1.0 - uvs[5]
		except:
			uv3x = 0.0
			uv3y = 0.0

I found i had to add the 1.0 - to the y values. I had to do that last time, but it seems to work fine so, i’m not going to question unless it breaks! Try it out anyways

i have no problems here to change uv coords when mesh is in object mode

and about adding new uv_layer: the tricks seems to be to add a new UV texture uv_textures.new(), which implicitly creates a uv_layer. Then set the .uv properties (seen in import_3ds.py)

Hello there.
Is there way to remove all UV’s from selected objects?
Such as

import bpy
for ob in bpy.context.selected_objects:
if ob.type = ‘MESH’:

Removing UV textures is supposed to also remoev UV layers:

import bpy
for ob in bpy.context.selected_editable_objects:
    if ob.type == 'MESH':
        for uv_tex in ob.data.uv_textures:
            ob.data.uv_textures.remove(uv_tex)

Oh, thank you!
I’ve found another way to remove all UV’s on objects:


import bpy
obj= bpy.context.selected_objects
for ob in obj:
    if ob.type == 'MESH':
        bpy.context.scene.objects.active = ob
        while(len(ob.data.uv_textures)):
            bpy.ops.mesh.uv_texture_remove()

Thanks midnight426 and CoDEmanX for those very useful Codesnipets !

Kind regards
Alain