diff options
Diffstat (limited to 'doc')
-rw-r--r-- | doc/python_api/examples/bpy.types.Depsgraph.1.py | 60 | ||||
-rw-r--r-- | doc/python_api/examples/bpy.types.Depsgraph.2.py | 45 | ||||
-rw-r--r-- | doc/python_api/examples/bpy.types.Depsgraph.3.py | 42 | ||||
-rw-r--r-- | doc/python_api/examples/bpy.types.Depsgraph.4.py | 62 | ||||
-rw-r--r-- | doc/python_api/examples/bpy.types.Depsgraph.5.py | 61 |
5 files changed, 270 insertions, 0 deletions
diff --git a/doc/python_api/examples/bpy.types.Depsgraph.1.py b/doc/python_api/examples/bpy.types.Depsgraph.1.py new file mode 100644 index 00000000000..d972c076c97 --- /dev/null +++ b/doc/python_api/examples/bpy.types.Depsgraph.1.py @@ -0,0 +1,60 @@ +""" +Dependency graph: Evaluated ID example +++++++++++++++++++++++++++++++++++++++ + +This example demonstrates access to the evaluated ID (such as object, material, etc.) state from +an original ID. +This is needed every time one needs to access state with animation, constraints, and modifiers +taken into account. +""" +import bpy + + +class OBJECT_OT_evaluated_example(bpy.types.Operator): + """Access evaluated object state and do something with it""" + bl_label = "DEG Access Evaluated Object" + bl_idname = "object.evaluated_example" + + def execute(self, context): + # This is an original object. Its data does not have any modifiers applied. + object = context.object + if object is None or object.type != 'MESH': + self.report({'INFO'}, "No active mesh object to get info from") + return {'CANCELLED'} + # Evaluated object exists within a specific dependency graph. + # We will request evaluated object from the dependency graph which corresponds to the + # current scene and view layer. + # + # NOTE: This call ensure the dependency graph is fully evaluated. This might be expensive + # if changes were made made to the scene, but is needed to ensure no dangling or incorrect + # pointers are exposed. + depsgraph = context.evaluated_depsgraph_get() + # Actually request evaluated object. + # + # This object has animation and drivers applied on it, together with constraints and + # modifiers. + # + # For mesh objects the object.data will be a mesh with all modifiers applied. + # This means that in access to vertices or faces after modifier stack happens via fields of + # object_eval.object. + # + # For other types of objects the object_eval.data does not have modifiers applied on it, + # but has animation applied. + # + # NOTE: All ID types have `evaluated_get()`, including materials, node trees, worlds. + object_eval = object.evaluated_get(depsgraph) + mesh_eval = object_eval.data + self.report({'INFO'}, f"Number of evaluated vertices: {len(mesh_eval.vertices)}") + return {'FINISHED'} + + +def register(): + bpy.utils.register_class(OBJECT_OT_evaluated_example) + + +def unregister(): + bpy.utils.unregister_class(OBJECT_OT_evaluated_example) + + +if __name__ == "__main__": + register() diff --git a/doc/python_api/examples/bpy.types.Depsgraph.2.py b/doc/python_api/examples/bpy.types.Depsgraph.2.py new file mode 100644 index 00000000000..8639ffc0267 --- /dev/null +++ b/doc/python_api/examples/bpy.types.Depsgraph.2.py @@ -0,0 +1,45 @@ +""" +Dependency graph: Original object example ++++++++++++++++++++++++++++++++++++++++++ + +This example demonstrates access to the original ID. +Such access is needed to check whether object is selected, or to compare pointers. +""" +import bpy + + +class OBJECT_OT_original_example(bpy.types.Operator): + """Access original object and do something with it""" + bl_label = "DEG Access Original Object" + bl_idname = "object.original_example" + + def check_object_selected(self, object_eval): + # Selection depends on a context and is only valid for original objects. This means we need + # to request the original object from the known evaluated one. + # + # NOTE: All ID types have an `original` field. + object = object_eval.original + return object.select_get() + + def execute(self, context): + # NOTE: It seems redundant to iterate over original objects to request evaluated ones + # just to get original back. But we want to keep example as short as possible, but in real + # world there are cases when evaluated object is coming from a more meaningful source. + depsgraph = context.evaluated_depsgraph_get() + for object in context.editable_objects: + object_eval = object.evaluated_get(depsgraph) + if self.check_object_selected(object_eval): + self.report({'INFO'}, f"Object is selected: {object_eval.name}") + return {'FINISHED'} + + +def register(): + bpy.utils.register_class(OBJECT_OT_original_example) + + +def unregister(): + bpy.utils.unregister_class(OBJECT_OT_original_example) + + +if __name__ == "__main__": + register() diff --git a/doc/python_api/examples/bpy.types.Depsgraph.3.py b/doc/python_api/examples/bpy.types.Depsgraph.3.py new file mode 100644 index 00000000000..25411597dd3 --- /dev/null +++ b/doc/python_api/examples/bpy.types.Depsgraph.3.py @@ -0,0 +1,42 @@ +""" +Dependency graph: Iterate over all object instances ++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Sometimes it is needed to know all the instances with their matrices (for example, when writing an +exporter or a custom render engine). +This example shows how to access all objects and instances in the scene. +""" +import bpy + + +class OBJECT_OT_object_instances(bpy.types.Operator): + """Access original object and do something with it""" + bl_label = "DEG Iterate Object Instances" + bl_idname = "object.object_instances" + + def execute(self, context): + depsgraph = context.evaluated_depsgraph_get() + for object_instance in depsgraph.object_instances: + # This is an object which is being instanced. + object = object_instance.object + # `is_instance` denotes whether the object is coming from instances (as an opposite of + # being an emitting object. ) + if not object_instance.is_instance: + print(f"Object {object.name} at {object_instance.matrix_world}") + else: + # Instanced will additionally have fields like uv, random_id and others which are + # specific for instances. See Python API for DepsgraphObjectInstance for details, + print(f"Instance of {object.name} at {object_instance.matrix_world}") + return {'FINISHED'} + + +def register(): + bpy.utils.register_class(OBJECT_OT_object_instances) + + +def unregister(): + bpy.utils.unregister_class(OBJECT_OT_object_instances) + + +if __name__ == "__main__": + register() diff --git a/doc/python_api/examples/bpy.types.Depsgraph.4.py b/doc/python_api/examples/bpy.types.Depsgraph.4.py new file mode 100644 index 00000000000..5c7b76edab6 --- /dev/null +++ b/doc/python_api/examples/bpy.types.Depsgraph.4.py @@ -0,0 +1,62 @@ +""" +Dependency graph: Object.to_mesh() ++++++++++++++++++++++++++++++++++++ + +Object.to_mesh() (and bpy.data.meshes.new_from_object()) are closely interacting with dependency +graph: their behavior depends on whether they are used on original or evaluated object. + +When is used on original object, the result mesh is calculated from the object without taking +animation or modifiers into account: + +- For meshes this is similar to duplicating the source mesh. +- For curves this disables own modifiers, and modifiers of objects used as bevel and taper. +- For metaballs this produces an empty mesh since polygonization is done as a modifier evaluation. + +When is used on evaluated object all modifiers are taken into account. + +.. note:: The result mesh is added to the main database. +.. note:: If object does not have geometry (i.e. camera) the functions returns None. +""" +import bpy + + +class OBJECT_OT_object_to_mesh(bpy.types.Operator): + """Convert selected object to mesh and show number of vertices""" + bl_label = "DEG Object to Mesh" + bl_idname = "object.object_to_mesh" + + def execute(self, context): + # Access input original object. + object = context.object + if object is None: + self.report({'INFO'}, "No active mesh object to convert to mesh") + return {'CANCELLED'} + # Avoid annoying None checks later on. + if object.type not in {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META'}: + self.report({'INFO'}, "Object can not be converted to mesh") + return {'CANCELLED'} + depsgraph = context.evaluated_depsgraph_get() + # Invoke to_mesh() for original object. + mesh_from_orig = object.to_mesh() + self.report({'INFO'}, f"{len(mesh_from_orig.vertices)} in new mesh without modifiers.") + # Remove temporary mesh. + bpy.data.meshes.remove(mesh_from_orig) + # Invoke to_mesh() for evaluated object. + object_eval = object.evaluated_get(depsgraph) + mesh_from_eval = object_eval.to_mesh() + self.report({'INFO'}, f"{len(mesh_from_eval.vertices)} in new mesh with modifiers.") + # Remove temporary mesh. + bpy.data.meshes.remove(mesh_from_eval) + return {'FINISHED'} + + +def register(): + bpy.utils.register_class(OBJECT_OT_object_to_mesh) + + +def unregister(): + bpy.utils.unregister_class(OBJECT_OT_object_to_mesh) + + +if __name__ == "__main__": + register() diff --git a/doc/python_api/examples/bpy.types.Depsgraph.5.py b/doc/python_api/examples/bpy.types.Depsgraph.5.py new file mode 100644 index 00000000000..781d0202931 --- /dev/null +++ b/doc/python_api/examples/bpy.types.Depsgraph.5.py @@ -0,0 +1,61 @@ +""" +Dependency graph: Simple exporter ++++++++++++++++++++++++++++++++++ + +This example is a combination of all previous ones, and shows how to write a simple exporter +script. +""" +import bpy + + +class OBJECT_OT_simple_exporter(bpy.types.Operator): + """Simple (fake) exporter of selected objects""" + bl_label = "DEG Export Selected" + bl_idname = "object.simple_exporter" + + apply_modifiers: bpy.props.BoolProperty(name="Apply Modifiers") + + def execute(self, context): + depsgraph = context.evaluated_depsgraph_get() + for object_instance in depsgraph.object_instances: + if not self.is_object_instance_from_selected(object_instance): + # We only export selected objects + continue + # NOTE: This will create a mesh for every instance, which is not ideal at all. In + # reality destination format will support some sort of instancing mechanism, so the + # code here will simply say "instance this object at object_instance.matrix_world". + mesh = self.create_mesh_for_object_instance(object_instance) + if mesh is None: + # Happens for non-geometry objects. + continue + print(f"Exporting mesh with {len(mesh.vertices)} vertices " + f"at {object_instance.matrix_world}") + bpy.data.meshes.remove(mesh) + + return {'FINISHED'} + + def is_object_instance_from_selected(self, object_instance): + # For instanced objects we check selection of their instancer (more accurately: check + # selection status of the original object corresponding to the instancer). + if object_instance.parent: + return object_instance.parent.original.select_get() + # For non-instanced objects we check selection state of the original object. + return object_instance.object.original.select_get() + + def create_mesh_for_object_instance(self, object_instance): + if self.apply_modifiers: + return object_instance.object.to_mesh() + else: + return object_instance.object.original.to_mesh() + + +def register(): + bpy.utils.register_class(OBJECT_OT_simple_exporter) + + +def unregister(): + bpy.utils.unregister_class(OBJECT_OT_simple_exporter) + + +if __name__ == "__main__": + register() |