I have made a script that:
- Uses a normal dot with two values to bake two stencils, then create and assign a new mat.
- Uses one of them with a texture (rocks..) to create a displace map
- Displaces the mesh
- Uses three textures (rocks, soil, grass) with the stencils to bake diffuse and normals and pack them
- replace the textures with the newly baked ones.
When I select more than one object it gives me a circular reference in texture stack every obj after the first, which means on of my image is in use by the object before baking. I troubleshot the problem to be in the very last part of my script which is assigning the normal texture to the material, so that when I comment that part off the script runs fine even with multiple objects selected.
So i guess I have to post it all, please have a look at it:
class gen():
def textures(i, c):
# Create the images for each object
bpy.context.scene.objects.active = i
bpy.ops.image.new(name = 'Stencil'+str(c))
bpy.ops.image.new(name = '2Stencil'+str(c))
bpy.ops.image.new(name = 'Displ'+str(c))
bpy.ops.image.new(name = 'Diffuse'+str(c))
bpy.ops.image.new(name = 'Normal'+str(c))
bpy.data.materials['Normal'].node_tree.nodes['Normal'].outputs[0].default_value = (0,1,1)
bpy.data.materials['Normal'].node_tree.nodes['Mapping'].scale[2] = 20
#assign the images and bake stencils from 'Normal'
for face in i.data.uv_textures.active.data:
face.image = bpy.data.images['Stencil' + str(c)]
bpy.data.scenes['Scene'].render.bake_type = 'TEXTURE'
bpy.ops.object.bake_image()
#needs 'Normal' for stencil
bpy.data.materials['Normal'].node_tree.nodes['Mapping'].scale[2] = 40
for face in i.data.uv_textures.active.data:
face.image = bpy.data.images['2Stencil' + str(c)]
bpy.ops.object.bake_image()
#Create and assign a new material ans textures for displacement
bpy.ops.material.new()
m = bpy.data.materials['Material']
m.name = 'Ground'+str(c)
m.texture_slots.add()
m.texture_slots.add()
m.texture_slots.add()
m.texture_slots.add()
m.texture_slots.add()
#get rid of original material
bpy.ops.object.material_slot_remove(0)
bpy.ops.object.material_slot_add()
i.material_slots[''].material = m
m.diffuse_color = (0,0,0)
#créer au préalable/needs une texture de 'Rochers', 'Herbe', 'Dirt'
m.active_texture_index = 4
m.texture_slots[4].texture = bpy.data.textures['Herbe']
m.texture_slots['Herbe'].texture_coords = 'UV'
m.texture_slots['Herbe'].use_map_color_spec = True
m.texture_slots['Herbe'].scale[0] = 5
m.texture_slots['Herbe'].scale[1] = 5
bpy.ops.texture.new()
t = bpy.data.textures['Texture']
t.name = 'Stencil'+str(c)
m.active_texture_index = 3
m.texture_slots[3].texture = bpy.data.textures['Stencil'+str(c)]
m.texture_slots['Stencil'+str(c)].texture_coords = 'UV'
m.active_texture.type = 'IMAGE'
m.active_texture.image = bpy.data.images['Stencil'+str(c)]
m.texture_slots['Stencil'+str(c)].use_rgb_to_intensity = True
m.texture_slots['Stencil'+str(c)].use_stencil = True
m.texture_slots['Stencil'+str(c)].use_map_color_diffuse = False
m.active_texture_index = 2
m.texture_slots[2].texture = bpy.data.textures['Rochers']
m.texture_slots['Rochers'].texture_coords = 'UV'
m.texture_slots['Rochers'].use_map_color_spec = True
bpy.ops.texture.new()
t = bpy.data.textures['Texture']
t.name = '2Stencil'+str(c)
m.active_texture_index = 1
m.texture_slots[1].texture = bpy.data.textures['2Stencil'+str(c)]
m.active_texture.type = 'IMAGE'
m.active_texture.image = bpy.data.images['2Stencil'+str(c)]
m.texture_slots['2Stencil'+str(c)].blend_type = 'SUBTRACT'
m.texture_slots['2Stencil'+str(c)].texture_coords = 'UV'
m.texture_slots['2Stencil'+str(c)].use_rgb_to_intensity = True
m.texture_slots['2Stencil'+str(c)].use_stencil = True
m.texture_slots['2Stencil'+str(c)].use_map_color_diffuse = False
m.active_texture_index = 0
m.texture_slots[0].texture = bpy.data.textures['Dirt']
m.texture_slots['Dirt'].texture_coords = 'UV'
m.texture_slots['Dirt'].use_map_color_spec = True
m.texture_slots['Dirt'].scale[0] = 5
m.texture_slots['Dirt'].scale[1] = 5
#deactivate the textures not needed for displace
m.use_textures[0] = False
m.use_textures[3] = False
m.use_textures[4] = False
#Bake
for face in i.data.uv_textures.active.data:
face.image = bpy.data.images['Displ' + str(c)]
bpy.ops.object.bake_image()
bpy.ops.object.modifier_add(type='DISPLACE')
i.modifiers['Displace'].direction = 'Z'
bpy.ops.texture.new()
t = bpy.data.textures['Texture']
t.name = 'Displ'+str(c)
bpy.data.textures['Displ'+str(c)].type = 'IMAGE'
bpy.data.textures['Displ'+str(c)].image = bpy.data.images['Displ'+str(c)]
i.modifiers['Displace'].texture = bpy.data.textures['Displ'+str(c)]
i.modifiers['Displace'].texture_coords = 'UV'
i.modifiers['Displace'].strength = 0.25
bpy.ops.object.modifier_apply(apply_as='DATA', modifier = 'Displace')
#Reactivate the textures, now the mesh is displaced
m.use_textures[0] = True
m.use_textures[3] = True
m.use_textures[4] = True
m.texture_slots['Stencil'+str(c)].invert = True
m.texture_slots['2Stencil'+str(c)].invert = True
for face in i.data.uv_textures.active.data:
face.image = bpy.data.images['Diffuse'+str(c)]
bpy.data.scenes['Scene'].render.bake_type = 'TEXTURE'
bpy.ops.object.bake_image()
bpy.data.images['Diffuse'+str(c)].pack(as_png=True)
for face in i.data.uv_textures.active.data:
face.image = bpy.data.images['Normal'+str(c)]
bpy.data.scenes['Scene'].render.bake_type = 'NORMALS'
bpy.ops.object.bake_image()
bpy.data.images['Normal'+str(c)].pack(as_png=True)
#Textures are baked and packed, now (maybe useless) deactivate all the slots
m.active_texture_index = 2
m.texture_slots[2].use = False
m.texture_slots.clear(2)
m.active_texture_index = 3
m.texture_slots[3].use = False
m.texture_slots.clear(3)
m.active_texture_index = 4
m.texture_slots[4].use = False
m.texture_slots.clear(4)
#Next assign newly created textures.
m.active_texture_index = 0
bpy.ops.texture.new()
t = bpy.data.textures['Texture']
t.name = 'Diffuse'+str(c)
t.type = 'IMAGE'
t.image = bpy.data.images['Diffuse'+str(c)]
m.texture_slots[0].texture = bpy.data.textures['Diffuse'+str(c)]
m.texture_slots[0].use_map_color_spec = True
m.texture_slots[0].texture_coords = 'UV'
'''
m.active_texture_index = 1
bpy.ops.texture.new()
t = bpy.data.textures['Texture']
t.name = 'Normal'+str(c)
t.type = 'IMAGE'
t.image = bpy.data.images['Normal'+str(c)]
m.texture_slots[1].texture = bpy.data.textures['Normal'+str(c)]
m.texture_slots[1].texture_coords = 'UV'
m.texture_slots[1].use_map_normal = True
m.texture_slots[1].use_map_color_diffuse = False
t.use_normal_map = True
m.texture_slots[1].normal_factor = 0.4
'''
def lod():
d = 1
for i in bpy.context.selected_objects:
bpy.context.scene.objects.active = i
bpy.ops.object.modifier_add(type='DECIMATE')
bpy.context.object.modifiers["Decimate"].ratio = 0.1
bpy.ops.object.modifier_apply(apply_as='DATA', modifier = 'Decimate')
bpy.context.object.name = i.name + str(d)
bpy.ops.object.duplicate()
bpy.ops.object.modifier_add(type='DECIMATE')
bpy.context.object.modifiers["Decimate"].ratio = 0.3
bpy.ops.object.modifier_apply(apply_as='DATA', modifier = 'Decimate')
bpy.context.object.name = i.name + str(d+1)
bpy.ops.object.duplicate()
bpy.ops.object.modifier_add(type='DECIMATE')
bpy.context.object.modifiers["Decimate"].ratio = 0.5
bpy.ops.object.modifier_apply(apply_as='DATA', modifier = 'Decimate')
bpy.context.object.name = i.name + str(d+2)
bpy.ops.object.duplicate()
bpy.ops.object.modifier_add(type='DECIMATE')
bpy.context.object.modifiers["Decimate"].ratio = 0.5
bpy.ops.object.modifier_apply(apply_as='DATA', modifier = 'Decimate')
bpy.context.object.name = i.name + str(d+3)
bpy.context.scene.objects.active = i
bpy.ops.object.lod_add()
bpy.ops.object.lod_add()
bpy.ops.object.lod_add()
i.lod_levels[1].object = bpy.data.objects[i.name+str(d+1)]
i.lod_levels[2].object = bpy.data.objects[i.name+str(d+2)]
i.lod_levels[3].object = bpy.data.objects[i.name+str(d+3)]
bpy.data.objects[i.name+str(d+1)].hide = True
bpy.data.objects[i.name+str(d+2)].hide = True
bpy.data.objects[i.name+str(d+3)].hide = True
d = d + 1
Gen = gen
c = 1
for i in bpy.context.selected_objects:
gen.textures(i, c)
c = c + 1
#gen.lod()
To run the script ; create a plane, unwrap, subdivide displace and everything you like, create a material node with a geometry input normal slot connected to a normal node, which has its dot connected to the factor of a mix, white above, black below ; AND you must add a mapping node between the geometry and normal nodes. Create three textures somewhere whose name should be 'Rochers' 'Dirt' and 'Herbe' corresponding to the textures of rocks, soil(dirt), and grass. Before running the script you should scale the plane up to eight time and apply all its modifiers and its scale. Of course use glsl shading.
There should not be a material named 'Material' nor a texture named 'Texture' in the scene.
bpy.ops.image.new(name = 'Normal'+str(c))
it will add an image named (forc=1
) "Normal1", the next time an image named "Normal1.001" and so on. If you are not cleaning out your images between runs, the script will try to use "Normal1" I would suggest using API methods likenormal_img = bpy.data.images.new(...)
$\endgroup$bpy.ops...new()
byobj = bpy.data...new(...)
. I share a paste of the code if it can help any. pastebin.com/n8snPg83 $\endgroup$