Extending the Simulator

While the BlenderService API, as typically accessed through a BlenderClient instance, provides a streamlined way to interact with Blender and facilitates most common operations, sometimes more fine-grained control is needed. For this, you can subclass BlenderService to access Blender’s internal bpy API.

Here we create a custom ExtendedService which allows for axis-aligned bounding box (AABB) calculations, and listing out any missing textures:

from pathlib import Path

import bpy
import numpy as np
from mathutils import Vector

from visionsim.simulate.blender import BlenderServer, BlenderService


class ExtendedService(BlenderService):
    def exposed_scene_aabb(self):
        aabb_min = np.array([np.inf, np.inf, np.inf])
        aabb_max = -np.array([np.inf, np.inf, np.inf])

        for obj in self.scene.objects:
            bbox = np.array(obj.bound_box)
            bbox_min = obj.matrix_world @ Vector(np.min(bbox, axis=0))
            bbox_max = obj.matrix_world @ Vector(np.max(bbox, axis=0))
            aabb_min = np.minimum(aabb_min, bbox_min)
            aabb_max = np.maximum(aabb_max, bbox_max)
        return aabb_min, aabb_max

    def exposed_missing_textures(self):
        paths = []

        for image in bpy.data.images.values():
            if image.source == "FILE":
                if not (path := Path(image.filepath_from_user()).resolve()).exists():
                    paths.append(path)

        return paths


if __name__ == "__main__":
    server = BlenderServer(service=ExtendedService, port=0)
    server.start()

Currently, in order to use this new rendering service, the user must spin it up manually (as opposed to using BlenderClient.spawn):

$ blender --background --python-use-system-env --python examples/blender/extended_service.py

And then connect to that render service, either directly using the appropriate connection settings, or using the BlenderClient.auto_connect:

with BlenderClient.auto_connect(timeout=30) as client:
    client.initialize("cube.blend", "renders/")
    print(client.scene_aabb())