I've created a script to add four cubes to a scene on two separate layers. I use object.location and object.rotation_euler to randomly position the objects in the scene.
When I render the scene at the end of the script not all of the objects are rendered in their new locations. It mostly seems that either the objects on layer 0 or those on layer 1 get stuck at the origin. Some of the "Scenes" (objects in different locations) render correctly, but others don't.
Interestingly, if I save the file before the render and open after the script is complete, all the objects are in the correct place. This confirms that the location and rotation changes are being set. I'm not sure when or how to make sure they are applied.
Research:
I've read a bunch of threads on this issue, but haven't found one that reliably solves the problem. In previous more complex models bpy.context.scene.update() had some effects, but didn't fix everything.
I've added bpy.context.update() before I save the file and render the scene with the objects on the different layers.
I have the same results if I run with bpy as a module (experimental), as a background job or from the Python console in Blender.
From the Blender Python console:
- import s2
- s2.run()
When I run this in the Blender Console I can see that the objects are not all updated in the 3D-View. If I save the file at this point and then revert I can see the objects move to the correct location.
I've been working around this by saving the file and then running a subprocess with a small script to render the scene. This is the equivalent of saving, and reopening the file.
It seems like I'm just missing something obvious here. All help is appreciated.
Sorry for the long example, but it took a bit to whittle it down to this.
import bpy
import os
from math import radians as radians
from random import random as random
from mathutils import Vector
root_dir_path = os.path.join(os.getenv("HOME"), "tmp/scratch")
if not os.path.exists(root_dir_path):
os.makedirs(root_dir_path)
obj_layer = 0
mask_layer = 1
camera_layer = 2
def clean_obj():
for obj in bpy.context.scene.objects:
if obj.name not in ['Camera', 'Lamp']:
bpy.context.scene.objects.unlink(obj)
for obj in bpy.data.objects:
if obj.name not in ['Camera', 'Lamp']:
bpy.data.objects.remove(obj)
def dump():
print("current state:")
print("\tObjects : ", bpy.data.objects.keys())
print("\tMeshes : ", bpy.data.meshes.keys())
print("\tMaterials: ", bpy.data.materials.keys())
print("\tTextures : ", bpy.data.textures.keys())
print("\tScenes : ", bpy.data.scenes.keys())
print("\tWorlds : ", bpy.data.worlds.keys())
def active_layers(layers):
mask = [False] * 20
for layer in layers:
mask[layer] = True
return mask
def render_scene(filepath, layers):
bpy.context.scene.camera = bpy.data.objects["Camera"]
bpy.context.scene.layers = active_layers(layers)
render = bpy.context.scene.render
render.use_file_extension = True
render.filepath = filepath
render.resolution_percentage = 100
render.tile_x = 480
render.tile_y = 270
render.image_settings.color_depth = '16'
render.image_settings.compression = 0
bpy.ops.render.render(write_still=True)
def run():
# Clean up
clean_obj()
camera_obj = bpy.data.objects["Camera"]
camera_obj.layers = active_layers([camera_layer])
camera_obj.location = Vector((0, 0, 20))
camera_obj.rotation_euler = (0, 0, 0)
bpy.data.objects["Lamp"].layers = active_layers([camera_layer])
num_obj = 2
num_scenes = 2
# Add 4 objects to the scene all at (0,0,0)
# Layer 0: Object-00, Object-01
# Layer 1: Object-00.Mask, Object-01.Mask
for i in range(num_obj):
scene = bpy.context.scene
# Add Object
obj_name = "Object-{:02d}".format(i)
print("Adding object: ", obj_name)
bpy.ops.mesh.primitive_cube_add(radius=1, view_align=False, enter_editmode=False,
location=(0, 0, 0), layers=active_layers([obj_layer]))
obj = scene.objects.active
obj.name = obj.data.name = obj_name
# Add Mask Object
obj_name = "{}.Mask".format(obj_name)
print("Adding object: ", obj_name)
bpy.ops.mesh.primitive_cube_add(radius=1, view_align=False, enter_editmode=False,
location=(0, 0, 0), layers=active_layers([mask_layer]))
obj = scene.objects.active
obj.name = obj.data.name = obj_name
dump()
for s in range(num_scenes):
for i in range(num_obj):
x = random() * 10 - 5
y = random() * 10 - 5
ry = random() * 360
rz = random() * 360
print((x, y, 0), (0, ry, rz))
ry = radians(ry)
rz = radians(rz)
obj_name = "Object-{:02d}".format(i)
obj = bpy.data.objects[obj_name]
obj.location = Vector((x, y, -2))
obj.rotation_euler = (0, ry, rz)
obj_name = "{}.Mask".format(obj_name)
obj = bpy.data.objects[obj_name]
obj.location = Vector((x, y, 2))
obj.rotation_euler = (0, ry, rz)
# Update the scene?
bpy.context.scene.update()
filepath = os.path.join(root_dir_path, "block-{:03d}.blend".format(s))
print("saving: ", filepath)
bpy.ops.wm.save_as_mainfile(filepath = filepath)
filepath = os.path.join(root_dir_path, "block-{:03d}.blend".format(s))
render_scene(filepath, [obj_layer, camera_layer])
filepath = os.path.join(root_dir_path, "mask-{:03d}.blend".format(s))
render_scene(filepath, [mask_layer, camera_layer])
if __name__ == '__main__':
run()