Is it possible to set the uv image through a script? Also, is it possible to run the cell fracture tool with a script as well.
not sure what you wanna do, but checkout the Material Utils addon
sure it’s possible, e.g.:
me = bpy.context.object.data
img = bpy.data.images[0]
uv_tex = me.uv_textures.active.data
for face in uv_tex:
face.image = img
I’m using the cell fracture tool to make breakable objects for Unity. Cell fracture can automatically apply a material in Blender for the inside of the fractured chunks, but Unity doesn’t work that way. It needs to have different textures for the uvs or it just lumps them all together. I was trying bpy.obj.images, but I guess Blender doesn’t store that information there.
yes, UV images are stored separately from per-face material assignments
I can’t seem to find the image though:
img = bpy.data.images[‘Table_Inside’]
should access the list of images to be linked?
is it really loaded with that name?
for img in bpy.data.images:
print(img.name)
Yes. Maybe it’s just not writing the image to the face uv.
I did a little debugging and it’s not getting into the loop:
for face in uv_tex:
face.image = img
well if there’s no UV texture layer, it won’t work, it has to be created before
It seems to have a texture layer (“UVMap”) which I’m guessing is the default. Here’s what I have so far:
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.select_all(action=‘INVERT’)
me = bpy.context.selected_objects[0].data
img = bpy.data.images[‘Table_Inside’]
me2 = me.tessfaces
uv_tex = me.uv_textures.active.data
i = 0
print(me)
print(me2)
print(uv_tex)
for tessfaces in uv_tex:
print(str(i))
face.image = img
i += 1
well, .tessfaces is usually empty, you need to ensure it’s synchronized with MPolys before you access it:
if not me.tessfaces and me.polygons:
me.calc_tessface()
and note that the UVs that belong to tessfaces are stored in tessface_uv_texture, but you shouldn’t try to change uv images there.
This should work better:
import bpy
ob = bpy.context.active_object
if ob is None or ob.type != 'MESH':
raise TypeError("Active object has to be a mesh ob")
if ob.mode != 'OBJECT':
raise RuntimeError("Mesh has to be in object mode")
me = ob.data
img = bpy.data.images[0]
if me.uv_textures.active is None:
uv_tex = me.uv_textures.new().data
else:
uv_tex = me.uv_textures.active.data
for poly in uv_tex:
poly.image = img
I’m a little confused as to why you why this only works in object mode. ob is still finding a mesh, but uv_tex contains no polys, faces, or verts in edit mode?
it does, but there are no UV entries available in editmode
Note that “for poly in uv_tex” means “for every entry in uv_tex”, and these entries are per-polygon, but not polygons.
So there’s no way to access only the selected faces in edit mode?
I got it to work about half the time. I ended up having to separate the mesh in edit mode and then apply the uv texture in object mode, and finally join the mesh back together. The problem is that it seems to randomly decide which chunk is the active object. Sometimes it’s the inside(which gives me the effect I’m looking for) and sometimes it’s the outside.
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.select_all(action=‘INVERT’)
ob = bpy.context.active_object
if ob is None or ob.type != ‘MESH’:
raise TypeError(“Active object has to be a mesh ob”)
bpy.ops.mesh.separate(type = ‘LOOSE’)
bpy.ops.object.editmode_toggle()
#if ob.mode != ‘OBJECT’:
raise RuntimeError(“Mesh has to be in object mode”)
me = ob.data
img = bpy.data.images[‘Table_Inside’]
if me.uv_textures.active is None:
uv_tex = me.uv_textures.new().data
print(uv_tex)
else:
uv_tex = me.uv_textures.active.data
for poly in uv_tex:
poly.image = img
bpy.ops.object.join()
ok, if you wanna assign to selected faces only, do:
import bpy
ob = bpy.context.active_object
if ob is None or ob.type != 'MESH':
raise TypeError("Active object has to be a mesh ob")
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='OBJECT')
me = ob.data
img = bpy.data.images[0]
if me.uv_textures.active is None:
uv_tex = me.uv_textures.new().data
else:
uv_tex = me.uv_textures.active.data
for poly, uvface in zip(me.polygons, uv_tex):
if poly.select:
poly.image = img
That may involve a mode change, if you wanna avoid that, you could use the bmesh module. It seems to support UV image assignments while staying in editmode, but i just experienced a possible problem with that while testing (after the change, UV editor doesn’t update automatically, and one time the assigned image showed in dropdown menu with red background and with 0 users, although i assigned it)
I’m a little wary of the bmesh module. My understanding is that it’s constantly changing and I don’t want to risk my script breaking with every Blender release. This is what ended up working for me:
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.select_all(action=‘INVERT’)
bpy.ops.mesh.separate(type = ‘LOOSE’)
bpy.ops.object.editmode_toggle()
ob = bpy.context.selected_objects[0]
if ob is None or ob.type != ‘MESH’:
raise TypeError(“Active object has to be a mesh ob”)
#ob.separate(type = ‘SELECTED’) # separate face in edit mode
#if ob.mode != ‘OBJECT’:
raise RuntimeError(“Mesh has to be in object mode”)
me = ob.data
img = bpy.data.images[‘Table_Inside’]
if me.uv_textures.active is None:
uv_tex = me.uv_textures.new().data
print(uv_tex)
else:
uv_tex = me.uv_textures.active.data
for poly in uv_tex:
poly.image = img
bpy.ops.object.join()
I still have some issues to resolve with it. I don’t understand why I can’t separate my mesh with type selected, only loose seems to work. Also, the join statement at the end is problematic if I try to put this script inside of a for loop to change the textures on multiple objects at once (like the 100 or so that come from a fractured mesh). It ends up joining them all into one big mesh.
This was my first time working with scripting in Blender and I’ve learned a lot from it. Thanks for all your help.
what’s wrong with my script? No splitting / joining required.
The Bmesh module is marked unstable, but the involved features for texture assignment won’t change drastically.
import bpy, bmesh
ob = bpy.context.object
img = bpy.data.images[0]
if ob.type != 'MESH':
raise TypeError
me = ob.data
if ob.mode == 'EDIT':
bm = bmesh.from_edit_mesh(me)
else:
bm = bmesh.new()
bm.from_mesh(me)
tex = bm.faces.layers.tex.active
if tex is None:
raise RuntimeError("Mesh has to be unwrapped already")
for face in bm.faces:
if face.select:
face[tex].image = img
if bm.is_wrapped:
bmesh.update_edit_mesh(me)
else:
bm.to_mesh(me)
me.update()
You’re script definitely works better than mine. I’ve been digging through the Blender api, but it’s tough to figure out exactly how
Blender manipulates it data. Anyways, thanks for all the help.