2
$\begingroup$

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:

  1. 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.

  2. I've added bpy.context.update() before I save the file and render the scene with the objects on the different layers.

  3. 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.

  4. From the Blender Python console:

    • import s2
    • s2.run()
  5. 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.

  6. 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()
$\endgroup$
  • $\begingroup$ More research: It seems that bpy.context.update() just isn't working like I expect it to. I can bring out the ugly brute force: 1. Change context to VIEW_3D 2. Select each object 3. Toggle into EDIT_MESH and back to OBJECT 4. Render When I render after this everything is in the right place. I'm currently using 2.79. I'll try a different version to see if it is something specific to 2.79. $\endgroup$ – Nathan Atkins Mar 26 '18 at 3:06
  • $\begingroup$ Got it! I have objects on multiple layers. In render_scene(): bpy.context.scene.layers is set to just the layers to render. When bpy.context.scene.update() is called it only updates the layers that are active. If all the layers that have had objects location changed are active then everything updates fine. If an object is moved on a layer that isn't active in the scene then its position is updated. Makes sense that scene.update() only updates things that are visible. $\endgroup$ – Nathan Atkins Mar 26 '18 at 15:31
2
$\begingroup$

bpy.context.scene.update() only updates the layers that are active in bpy.context.scene.layers. Make sure that the layers that are going to be rendered - set with bpy.context.scene.layers are the same ones that are active when scene.update() is called.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.