Skip to content

Blender 2.80: Scene and Object API

View Layer & Collection

2.8x collections replace both groups and the fixed twenty scene's layers of 2.7x. That means that objects belonging to a collection can both be instantiated (from another Empty object, on particles, etc.), and organized as regular objects in a scene, when their collection is linked into the scene's hierarchy of collections.

Each scene has a master collection, which is part of the scene data, and not in the Main database (i.e. you won't find it in bpy.data.collections, pretty much like root node trees of materials etc.). It is the root of the collections tree of that scene.

While in 2.7x layer belonging was defined in the object, in 2.8x the object must be linked to the desired collections. And while in 2.7x you always had twenty layers, in 2.8x you have to ensure you have desired collections yourself.

2.7x:

# Add active object to the second layer.
C.object.layers[1] = True

2.8x:

# Create a new collection and link it to the scene.
coll2 = D.collections.new("Collection 2")
C.scene.collection.children.link(coll2)
# Link active object to the new collection
coll2.objects.link(C.object)

2.8x view layers replace and extend 2.7x render layers. The main difference is that they also control what is visible in the scene, not only for rendering, and the whole organization of collections.

View layers replicate the collection hierarchy of their scene, but they give access to a light wrapper around those, LayerCollection, which among other things allow to exclude a given collection from a given view layer. By default, all collections linked to a scene will be part, active in all its view layers.

2.7x:

# Exclude layer 2 from rendering in active render layer.
C.scene.render.layers.active.layers[1] = False

2.8x:

# Exclude Collection 2 we created above from active view layer.
C.window.view_layer.layer_collection.children["Collection 2"].exclude = True

Note that in 2.8x, you get the active view layer from the active window, not the active scene.

Scene update

2.7x:

# Update scene
scene=bpy.context.scene
scene.update()

2.8x:

# Update view layer
layer = bpy.context.view_layer
layer.update()

Object Selection and Hiding

In 2.7x, you could directly (de)select an Object from its select property. This has been removed in 2.8x, in favor of some get/set functions.

2.7x:

ob = bpy.context.object
if not ob.select:
    ob.select = True

2.8x:

ob = bpy.context.object
if not ob.select_get():
    ob.select_set(True)

See the API documentation for details.

Hiding works nearly the same in 2.8x than in 2.7x, the only difference is that, for consistency, the hide property has been renamed to hide_viewport.

2.7x:

ob = bpy.context.object
print("Object %s:\n\thide in viewport: %r\n\thide in render: %r\n\tis selectable: %r\n" %
      (ob.name, ob.hide, ob.hide_render, not ob.hide_select))

2.8x:

ob = bpy.context.object
print("Object %s:\n\thide in viewport: %r\n\thide in render: %r\n\tis selectable: %r\n" %
      (ob.name, ob.hide_viewport, ob.hide_render, not ob.hide_select))

However, collection hide status also matters in 2.8x. An object only linked in hidden collections will always be hidden, regardless of its own flag setting. This is similar to hiding a layer in 2.7x.

Adding Objects

In 2.7x objects were added to (and owned by) a Scene. In 2.8x, they are added to (and owned by) a Collection. See above for details about collections and view layers.

2.7x:

scene = bpy.context.scene
scene.objects.link(ob)

2.8x:

scene = bpy.context.scene
# Links object to the master collection of the scene.
scene.collection.objects.link(ob)

A same object can be in as many collections as you want - but it will still be a single object (a single instance). An object will be hidden from a scene only if it is hidden itself, or if all the collections it belongs to are hidden, or excluded from active view layer.

Note that scene.objects still exists in 2.8x, but as a mere read-only flat list of all objects instantiated in the scene, you cannot directly edit it anymore in any way.

Instancing

Access to dupli instances has completely changed. In 2.7x, you could access all instances generated from one object like this:

2.7x:

ob = context.object
ob.dupli_list_create(scene, 'PREVIEW')
for dup in ob.dupli_list:
    # Do whatever you want with this dupli, even store it in a temp variable
    # (as long as dupli_list is not cleared).
    pass
# ...

# After this point, any reference to dupli-objects would become invalid.
ob.dupli_list_clear()

In 2.8x, instances are only available inside of the depsgraph context. So you do not need to create them anymore, just loop over them. But you need a depsgraph to do this.

2.8x:

depsgraph = context.evaluated_depsgraph_get()
for ob_inst in depsgraph.object_instances:
    # Do whatever you want with this instance, but DO NOT STORE any reference to it!
    pass

WARNING: Do not store any reference to this dupli, nor any of its IDs. That data is generated on the fly, and becomes invalid as soon as you iterate to the next item or leave the iterator context. Furthermore, ID RNA pointers will point to evaluated ones. You must duplicate any data (like dupli matrix etc.) if you want to store it.

Another big difference is that you do not have duplis per objects anymore, only the whole lot of instances in the current depsgraph. This includes 'real' object ones:

2.8x:

depsgraph = context.evaluated_depsgraph_get()
for ob_inst in depsgraph.object_instances:
    if ob_inst.is_instance:  # Real dupli instance
        ob = ob_inst.instance_object.original
        parent = ob_inst.parent.original
        mat = ob_inst.matrix_world.copy()
    else:  # Usual object
        ob = ob_inst.object.original
    # Do whatever you want with ob, parent, and mat.
    # These are a local copy or original data-blocks.

You can get the duplis of a specific object with this kind of iterator:

depsgraph = context.evaluated_depsgraph_get()
for ob_inst in depsgraph.object_instances:
    if ob_inst.parent and ob_inst.parent.original == context.object:
       # Do whatever you want with 'ob_inst'.
        pass

A depsgraph can currently be retrieved from a Context or ViewLayer, but there is no way to get a 'render' depsgraph for now. The API will have more advanced handling of depsgraphes in the future, but at this time it is still WIP.

Multi-Object Modes

Multiple objects can now be in edit and pose mode at the same time, whereas previously there was only one object. Operators need to be updated to affect all objects in the active mode.

To test which mode is active, use context.mode. Accessing objects in this mode can be done with two new context attributes:

  • context.objects_in_mode: list of all objects in the mode.
  • context.objects_in_mode_unique_data: when multiple objects share the same data, list only one object per unique object data. This is the most commonly used attribute, as usually each mesh or other object data should be edited once.

Base and context override

We no longer expose ObjectBase in the Python API, so we removed scene.object_bases. That means when overriding the context for certain operator calls there is no need to set:

  • visible_bases
  • selectable_bases
  • editable_bases
  • selected_editable_bases
  • active_base