# UV unwrap each face of an object via python script

I have an object with several faces, and I want to loop over the faces, adjust the view for each differently and then UV unwrap the face from the chosen view.

I managed to do it using Blender GUI but how can I automate it via a python script?

This is the code I was trying with... I keep getting either a context error or an error saying that BMFace has been removed :s

import bpy, bmesh
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
bm = bmesh.from_edit_mesh(bpy.data.objects['my_obj'].data)
for face in bm.faces:
face.select = True
if [...] : # Condition chosen to know which face this is
q = Quaternion([...]) # Set desired corresponding view for this face
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
break
for region in area.regions:
if region.type == 'WINDOW':
break
space = area.spaces[0]
context = bpy.context.copy()
context['area'] = area
context['region'] = region
context['space_data'] = space
r3d = space.region_3d
r3d.view_perspective = 'ORTHO'
r3d.view_rotation = q

for window in bpy.context.window_manager.windows:
screen = window.screen
for area in screen.areas:
if area.type == 'VIEW_3D':
for space in area.spaces:
if space.type == 'VIEW_3D':
for region in area.regions:
if region.type == 'WINDOW':
override = {'window': window, 'screen': screen, 'area': area, 'region': region}
bpy.ops.uv.project_from_view(override, orthographic=False, correct_aspect=True, clip_to_bounds=False, scale_to_bounds=True)
face.select = False


Manipulating UV coordinates in python is usually done via the bmesh interface.

I include an example from barber-pole.py

def spiralUVs(mesh, xPlus):
# add a UV layer called "spiral" and make it slanted.
mesh.uv_textures.new("spiral")
bm = bmesh.new()
bm.from_mesh(mesh)

bm.faces.ensure_lookup_table()

uv_layer = bm.loops.layers.uv[0]

nFaces = len(bm.faces)
for fi in range(nFaces):
x0 = fi*2/nFaces
x1 = (fi+1)*2/nFaces
bm.faces[fi].loops[0][uv_layer].uv = (x0, 0)
bm.faces[fi].loops[1][uv_layer].uv = (x1, 0)
bm.faces[fi].loops[2][uv_layer].uv = (xPlus+x1, 1)
bm.faces[fi].loops[3][uv_layer].uv = (xPlus+x0, 1)
bm.to_mesh(mesh)


Some other examples that set UV texture coordinates include

• Thanks a lot! I just got rid of this project_from_view function and set the values of the uv coordinates directly as desired :) – Beginner Jan 25 '16 at 8:28

You could try to create an operator that is executed in the correct context, then you don't need to search for the right one.

Something like this:

import bpy
import mathutils

def main(context):
# Your code without context searching
bpy.ops.uv.project_from_view()
context.space_data.region_3d.view_rotation = mathutils.Quaternion((1,1,1))

class SimpleOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.simple_operator"
bl_label = "Simple Object Operator"

@classmethod
def poll(cls, context):
return context.mode == 'EDIT_MESH'

def execute(self, context):
main(context)
return {'FINISHED'}

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

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

if __name__ == "__main__":
register()


You can then execute the operator in your 3d-window (Space -> Simple Object Operator).

• I am not used to bpy operators, but I don't see how this operator manages to be called in the correct context. Note that I need the whole thing to be fully automated in my case... no GUI at all. – Beginner Jan 25 '16 at 8:33
• @Beginner any update if it worked for you without GUI using this? – gonephishing Mar 27 '18 at 9:52

This is not a script, but it may get what you want.

Select all faces (a) then unwrap (u). In the unwrap menu, select 'reset'. This will make every face fill up the whole uv grid. Each face will be a square, and fit your texture.

Hope this helps.

• Thanks for the advice :) but it is not applicable in my case – Beginner Jan 25 '16 at 8:35