Dependency graph API changes
authorSergey Sharybin <sergey.vfx@gmail.com>
Thu, 9 May 2019 09:26:49 +0000 (11:26 +0200)
committerSergey Sharybin <sergey.vfx@gmail.com>
Thu, 16 May 2019 09:49:21 +0000 (11:49 +0200)
Main goal here is to make it obvious and predictable about
what is going on.

Summary of changes.

- Access to dependency graph is now only possible to a fully evaluated
  graph. This is now done via context.evaluated_depsgraph_get().

  The call will ensure both relations and datablocks are updated.

  This way we don't allow access to some known bad state of the graph,
  and also making explicit that getting update dependency graph is not
  cheap.

- Access to evaluated ID is now possible via id.evaluated_get().

  It was already possible to get evaluated ID via dependency graph,
  but that was a bit confusing why access to original is done via ID
  and to evaluated via depsgraph.

  If datablock is not covered by dependency graph it will be returned
  as-is.

- Similarly, request for original from an ID which is not evaluated
  will return ID as-is.

- Removed scene.update().

  This is very expensive to update all the view layers.

- Added depsgraph.update().

  Now when temporary changes to objects are to be done, this is to
  happen on original object and then dependency graph is to be
  updated.

- Changed object.to_mesh() to behave the following way:

   * When is used for original object modifiers are ignored.

     For meshes this acts similar to mesh-copy, not very useful but
     allows to keep code paths similar (i.e. for exporter which has
     Apply Modifiers option it's only matter choosing between original
     and evaluated object, the to_mesh() part can stay the same).

     For curves this gives a mesh which is constructed from displist
     without taking own modifiers and modifiers of bevel/taper objects
     into account.

     For metaballs this gives empty mesh.
     Polygonization of metaball is not possible from a single object.

   * When is used for evaluated object modifiers are always applied.

     In fact, no evaluation is happening, the mesh is either copied
     as-is, or constructed from current state of curve cache.

  Arguments to apply modifiers and calculate original coordinates (ORCO,
  aka undeformed coordinates) are removed. The ORCO is to be calculated
  as part of dependency graph evaluation.

File used to regression-test (a packed Python script into .blend):

{F7033464}

Patch to make addons tests to pass:

{F7033466}

NOTE: I've included changes to FBX exporter, and those are addressing
report T63689.

NOTE: All the enabled-by-default addons are to be ported still, but
first want to have agreement on this part of changes.

NOTE: Also need to work on documentation for Python API, but, again,
better be done after having agreement on this work.

Reviewers: brecht, campbellbarton, mont29

Differential Revision: https://developer.blender.org/D4834

21 files changed:
doc/python_api/examples/bpy.types.Depsgraph.1.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Depsgraph.2.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Depsgraph.3.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Depsgraph.4.py [new file with mode: 0644]
doc/python_api/examples/bpy.types.Depsgraph.5.py [new file with mode: 0644]
intern/cycles/blender/addon/__init__.py
intern/cycles/blender/blender_util.h
source/blender/blenkernel/BKE_context.h
source/blender/blenkernel/BKE_mesh.h
source/blender/blenkernel/intern/context.c
source/blender/blenkernel/intern/mesh_convert.c
source/blender/editors/object/object_bake_api.c
source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
source/blender/makesrna/intern/rna_ID.c
source/blender/makesrna/intern/rna_context.c
source/blender/makesrna/intern/rna_depsgraph.c
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_main_api.c
source/blender/makesrna/intern/rna_object_api.c
source/blender/makesrna/intern/rna_scene_api.c
tests/python/bl_alembic_import_test.py

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 (file)
index 0000000..d972c07
--- /dev/null
@@ -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 (file)
index 0000000..8639ffc
--- /dev/null
@@ -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 (file)
index 0000000..2541159
--- /dev/null
@@ -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 (file)
index 0000000..5c7b76e
--- /dev/null
@@ -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 (file)
index 0000000..781d020
--- /dev/null
@@ -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()
index 93a1271..a8e7428 100644 (file)
@@ -88,15 +88,17 @@ class CyclesRender(bpy.types.RenderEngine):
 
     # viewport render
     def view_update(self, context):
+        depsgraph = context.evaluated_depsgraph_get()
         if not self.session:
             engine.create(self, context.blend_data,
                           context.region, context.space_data, context.region_data)
 
-        engine.reset(self, context.blend_data, context.depsgraph)
-        engine.sync(self, context.depsgraph, context.blend_data)
+        engine.reset(self, context.blend_data, depsgraph)
+        engine.sync(self, depsgraph, context.blend_data)
 
     def view_draw(self, context):
-        engine.draw(self, context.depsgraph, context.region, context.space_data, context.region_data)
+        depsgraph = context.evaluated_depsgraph_get()
+        engine.draw(self, depsgraph, context.region, context.space_data, context.region_data)
 
     def update_script_node(self, node):
         if engine.with_osl():
index 500634e..2a964d0 100644 (file)
@@ -45,8 +45,8 @@ void python_thread_state_restore(void **python_thread_state);
 
 static inline BL::Mesh object_to_mesh(BL::BlendData &data,
                                       BL::Object &object,
-                                      BL::Depsgraph &depsgraph,
-                                      bool calc_undeformed,
+                                      BL::Depsgraph & /*depsgraph*/,
+                                      bool /*calc_undeformed*/,
                                       Mesh::SubdivisionType subdivision_type)
 {
   /* TODO: make this work with copy-on-write, modifiers are already evaluated. */
@@ -75,11 +75,11 @@ static inline BL::Mesh object_to_mesh(BL::BlendData &data,
      * UV are not empty. */
     if (mesh.is_editmode() ||
         (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) {
-      mesh = data.meshes.new_from_object(depsgraph, object, false, false);
+      mesh = data.meshes.new_from_object(object);
     }
   }
   else {
-    mesh = data.meshes.new_from_object(depsgraph, object, true, calc_undeformed);
+    mesh = data.meshes.new_from_object(object);
   }
 
 #if 0
index 8be43a5..0d2998c 100644 (file)
@@ -311,8 +311,23 @@ int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list);
 int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list);
 int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list);
 
+/* Gets pointer to the dependency graph.
+ * If it doesn't exist yet, it will be allocated.
+ *
+ * The result dependency graph is NOT guaranteed to be up-to-date neither from relation nor from
+ * evaluated data points of view.
+ *
+ * NOTE: Can not be used if access to a fully evaluated datablock is needed. */
 struct Depsgraph *CTX_data_depsgraph(const bContext *C);
 
+/* Gets fully updated and evaluated dependency graph.
+ *
+ * All the relations and evaluated objects are guaranteed to be up to date.
+ *
+ * NOTE: Will be expensive if there are relations or objects tagged for update.
+ * NOTE: If there are pending updates depsgraph hooks will be invoked. */
+struct Depsgraph *CTX_data_evaluated_depsgraph(const bContext *C);
+
 /* Will Return NULL if depsgraph is not allocated yet.
  * Only used by handful of operators which are run on file load.
  */
index b094dc5..8ea5445 100644 (file)
@@ -208,12 +208,7 @@ float (*BKE_mesh_vertexCos_get(const struct Mesh *me, int *r_numVerts))[3];
 
 void BKE_mesh_split_faces(struct Mesh *mesh, bool free_loop_normals);
 
-struct Mesh *BKE_mesh_new_from_object(struct Depsgraph *depsgraph,
-                                      struct Main *bmain,
-                                      struct Scene *sce,
-                                      struct Object *ob,
-                                      const bool apply_modifiers,
-                                      const bool calc_undeformed);
+struct Mesh *BKE_mesh_new_from_object(struct Main *bmain, struct Object *object);
 struct Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
                                                   struct Scene *scene,
                                                   struct Object *ob_eval,
index 4383b6a..dd326ba 100644 (file)
@@ -1315,6 +1315,14 @@ Depsgraph *CTX_data_depsgraph(const bContext *C)
   return BKE_scene_get_depsgraph(scene, view_layer, true);
 }
 
+Depsgraph *CTX_data_evaluated_depsgraph(const bContext *C)
+{
+  Depsgraph *depsgraph = CTX_data_depsgraph(C);
+  Main *bmain = CTX_data_main(C);
+  BKE_scene_graph_update_tagged(depsgraph, bmain);
+  return depsgraph;
+}
+
 Depsgraph *CTX_data_depsgraph_on_load(const bContext *C)
 {
   Scene *scene = CTX_data_scene(C);
index fe8d053..a5c97cd 100644 (file)
@@ -892,302 +892,219 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene),
   }
 }
 
-/* settings: 1 - preview, 2 - render
+/* Create a temporary object to be used for nurbs-to-mesh conversion.
  *
- * The convention goes as following:
- *
- * - Passing original object with apply_modifiers=false will give a
- *   non-modified non-deformed mesh.
- *   The result mesh will point to datablocks from the original "domain". For
- *   example, materials will be original.
- *
- * - Passing original object with apply_modifiers=true will give a mesh which
- *   has all modifiers applied.
- *   The result mesh will point to datablocks from the original "domain". For
- *   example, materials will be original.
- *
- * - Passing evaluated object will ignore apply_modifiers argument, and the
- *   result always contains all modifiers applied.
- *   The result mesh will point to an evaluated datablocks. For example,
- *   materials will be an evaluated IDs from the dependency graph.
- */
-Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
-                               Main *bmain,
-                               Scene *sce,
-                               Object *ob,
-                               const bool apply_modifiers,
-                               const bool calc_undeformed)
+ * This is more complex that it should be because BKE_mesh_from_nurbs_displist() will do more than
+ * simply conversion and will attempt to take over ownership of evaluated result and will also
+ * modify the input object. */
+static Object *object_for_curve_to_mesh_create(Object *object)
 {
-  Mesh *tmpmesh;
-  Curve *tmpcu = NULL, *copycu;
-  int i;
-  const bool render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
-  bool effective_apply_modifiers = apply_modifiers;
-  bool do_mat_id_data_us = true;
-
-  Object *object_input = ob;
-  Object *object_eval = DEG_get_evaluated_object(depsgraph, object_input);
-  Object object_for_eval;
-
-  if (object_eval == object_input) {
-    /* Evaluated mesh contains all modifiers applied already.
-     * The other types of object has them applied, but are stored in other
-     * data structures than a mesh. So need to apply modifiers again on a
-     * temporary copy before converting result to mesh. */
-    if (object_input->type == OB_MESH) {
-      effective_apply_modifiers = false;
-    }
-    else {
-      effective_apply_modifiers = true;
-    }
-    object_for_eval = *object_eval;
+  Curve *curve = (Curve *)object->data;
+
+  /* Create object itself. */
+  Object *temp_object;
+  BKE_id_copy_ex(NULL, &object->id, (ID **)&temp_object, LIB_ID_COPY_LOCALIZE);
+
+  /* Remove all modifiers, since we don't want them to be applied. */
+  BKE_object_free_modifiers(temp_object, LIB_ID_CREATE_NO_USER_REFCOUNT);
+
+  /* Copy relevant evaluated fields of curve cache.
+   *
+   * Note that there are extra fields in there like bevel and path, but those are not needed during
+   * conversion, so they are not copied to save unnecessary allocations. */
+  if (object->runtime.curve_cache != NULL) {
+    temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache),
+                                                   "CurveCache for curve types");
+    BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp);
   }
-  else {
-    if (apply_modifiers) {
-      object_for_eval = *object_eval;
-      if (object_for_eval.runtime.mesh_orig != NULL) {
-        object_for_eval.data = object_for_eval.runtime.mesh_orig;
-      }
-    }
-    else {
-      object_for_eval = *object_input;
-    }
+  /* Constructive modifiers will use mesh to store result. */
+  if (object->runtime.mesh_eval != NULL) {
+    BKE_id_copy_ex(NULL,
+                   &object->runtime.mesh_eval->id,
+                   (ID **)&temp_object->runtime.mesh_eval,
+                   LIB_ID_COPY_LOCALIZE);
   }
 
-  const bool cage = !effective_apply_modifiers;
+  /* Need to create copy of curve itself as well, it will be freed by underlying conversion
+   * functions.
+   *
+   * NOTE: Copies the data, but not the shapekeys. */
+  BKE_id_copy_ex(NULL, object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE);
+  Curve *temp_curve = (Curve *)temp_object->data;
 
-  /* perform the mesh extraction based on type */
-  switch (object_for_eval.type) {
-    case OB_FONT:
-    case OB_CURVE:
-    case OB_SURF: {
-      ListBase dispbase = {NULL, NULL};
-      Mesh *me_eval_final = NULL;
-      int uv_from_orco;
-
-      /* copies object and modifiers (but not the data) */
-      Object *tmpobj;
-      BKE_id_copy_ex(NULL, &object_for_eval.id, (ID **)&tmpobj, LIB_ID_COPY_LOCALIZE);
-      tmpcu = (Curve *)tmpobj->data;
-
-      /* Copy cached display list, it might be needed by the stack evaluation.
-       * Ideally stack should be able to use render-time display list, but doing
-       * so is quite tricky and not safe so close to the release.
-       *
-       * TODO(sergey): Look into more proper solution.
-       */
-      if (object_for_eval.runtime.curve_cache != NULL) {
-        if (tmpobj->runtime.curve_cache == NULL) {
-          tmpobj->runtime.curve_cache = MEM_callocN(sizeof(CurveCache),
-                                                    "CurveCache for curve types");
-        }
-        BKE_displist_copy(&tmpobj->runtime.curve_cache->disp,
-                          &object_for_eval.runtime.curve_cache->disp);
-      }
+  /* Make sure texture space is calculated for a copy of curve, it will be used for the final
+   * result. */
+  BKE_curve_texspace_calc(temp_curve);
 
-      /* if getting the original caged mesh, delete object modifiers */
-      if (cage) {
-        BKE_object_free_modifiers(tmpobj, LIB_ID_CREATE_NO_USER_REFCOUNT);
-      }
+  /* Temporarily set edit so we get updates from edit mode, but also because for text datablocks
+   * copying it while in edit mode gives invalid data structures. */
+  temp_curve->editfont = curve->editfont;
+  temp_curve->editnurb = curve->editnurb;
 
-      /* copies the data, but *not* the shapekeys. */
-      BKE_id_copy_ex(NULL, object_for_eval.data, (ID **)&copycu, LIB_ID_COPY_LOCALIZE);
-      tmpobj->data = copycu;
-
-      /* make sure texture space is calculated for a copy of curve,
-       * it will be used for the final result.
-       */
-      BKE_curve_texspace_calc(copycu);
-
-      /* temporarily set edit so we get updates from edit mode, but
-       * also because for text datablocks copying it while in edit
-       * mode gives invalid data structures */
-      copycu->editfont = tmpcu->editfont;
-      copycu->editnurb = tmpcu->editnurb;
-
-      /* get updated display list, and convert to a mesh */
-      BKE_displist_make_curveTypes_forRender(
-          depsgraph, sce, tmpobj, &dispbase, &me_eval_final, false, NULL);
-
-      copycu->editfont = NULL;
-      copycu->editnurb = NULL;
-
-      tmpobj->runtime.mesh_eval = me_eval_final;
-
-      /* convert object type to mesh */
-      uv_from_orco = (tmpcu->flag & CU_UV_ORCO) != 0;
-      BKE_mesh_from_nurbs_displist(
-          bmain, tmpobj, &dispbase, uv_from_orco, tmpcu->id.name + 2, true);
-      /* Function above also frees copycu (aka tmpobj->data), make this obvious here. */
-      copycu = NULL;
-
-      tmpmesh = tmpobj->data;
-      id_us_min(
-          &tmpmesh->id); /* Gets one user from its creation in BKE_mesh_from_nurbs_displist(). */
-
-      BKE_displist_free(&dispbase);
-
-      /* BKE_mesh_from_nurbs changes the type to a mesh, check it worked.
-       * if it didn't the curve did not have any segments or otherwise
-       * would have generated an empty mesh */
-      if (tmpobj->type != OB_MESH) {
-        BKE_id_free(NULL, tmpobj);
-        return NULL;
-      }
-
-      BKE_id_free(NULL, tmpobj);
+  return temp_object;
+}
 
-      /* XXX The curve to mesh conversion is convoluted...
-       *     But essentially, BKE_mesh_from_nurbs_displist()
-       *     already transfers the ownership of materials from the temp copy of the Curve ID to the
-       *     new Mesh ID, so we do not want to increase materials' usercount later. */
-      do_mat_id_data_us = false;
+static void curve_to_mesh_eval_ensure(Object *object)
+{
+  if (object->runtime.curve_cache == NULL) {
+    object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
+  }
+  Curve *curve = (Curve *)object->data;
+  Curve remapped_curve = *curve;
+  Object remapped_object = *object;
+  remapped_object.data = &remapped_curve;
 
-      break;
-    }
+  /* Clear all modifiers for the bevel object.
+   *
+   * This is because they can not be reliably evaluated for an original object (at least because
+   * the state of dependencies is not know).
+   *
+   * So we create temporary copy of the object which will use same data as the original bevel, but
+   * will have no modifiers. */
+  Object bevel_object = {NULL};
+  if (remapped_curve.bevobj != NULL) {
+    bevel_object = *remapped_curve.bevobj;
+    BLI_listbase_clear(&bevel_object.modifiers);
+    remapped_curve.bevobj = &bevel_object;
+  }
 
-    case OB_MBALL: {
-      /* metaballs don't have modifiers, so just convert to mesh */
-      Object *basis_ob = BKE_mball_basis_find(sce, object_input);
-      /* todo, re-generatre for render-res */
-      /* metaball_polygonize(scene, ob) */
+  /* Same thing for taper. */
+  Object taper_object = {NULL};
+  if (remapped_curve.taperobj != NULL) {
+    taper_object = *remapped_curve.taperobj;
+    BLI_listbase_clear(&taper_object.modifiers);
+    remapped_curve.taperobj = &taper_object;
+  }
 
-      if (basis_ob != object_input) {
-        /* Only do basis metaball. */
-        return NULL;
-      }
+  /* NOTE: We don't have dependency graph or scene here, so we pass NULL. This is all fine since
+   * they are only used for modifier stack, which we have explicitly disabled for all objects.
+   *
+   * TODO(sergey): This is a very fragile logic, but proper solution requires re-writing quite a
+   * bit of internal functions (BKE_mesh_from_nurbs_displist, BKE_mesh_nomain_to_mesh) and also
+   * Mesh From Curve operator.
+   * Brecht says hold off with that. */
+  BKE_displist_make_curveTypes_forRender(NULL,
+                                         NULL,
+                                         &remapped_object,
+                                         &remapped_object.runtime.curve_cache->disp,
+                                         &remapped_object.runtime.mesh_eval,
+                                         false,
+                                         NULL);
+
+  BKE_object_free_curve_cache(&bevel_object);
+  BKE_object_free_curve_cache(&taper_object);
+}
 
-      tmpmesh = BKE_mesh_add(bmain, ((ID *)object_for_eval.data)->name + 2);
-      /* BKE_mesh_add gives us a user count we don't need */
-      id_us_min(&tmpmesh->id);
+static Mesh *mesh_new_from_curve_type_object(Main *bmain, Object *object)
+{
+  Curve *curve = object->data;
+  const bool uv_from_orco = (curve->flag & CU_UV_ORCO) != 0;
 
-      if (render) {
-        ListBase disp = {NULL, NULL};
-        BKE_displist_make_mball_forRender(depsgraph, sce, &object_for_eval, &disp);
-        BKE_mesh_from_metaball(&disp, tmpmesh);
-        BKE_displist_free(&disp);
-      }
-      else {
-        ListBase disp = {NULL, NULL};
-        if (object_for_eval.runtime.curve_cache) {
-          disp = object_for_eval.runtime.curve_cache->disp;
-        }
-        BKE_mesh_from_metaball(&disp, tmpmesh);
-      }
+  Object *temp_object = object_for_curve_to_mesh_create(object);
+  Curve *temp_curve = (Curve *)temp_object->data;
 
-      BKE_mesh_texspace_copy_from_object(tmpmesh, &object_for_eval);
+  /* When input object is an original one, we don't have evaluated curve cache yet, so need to
+   * create it in the temporary object. */
+  if (!DEG_is_evaluated_object(object)) {
+    curve_to_mesh_eval_ensure(temp_object);
+  }
 
-      break;
-    }
-    case OB_MESH:
-      /* copies object and modifiers (but not the data) */
-      if (cage) {
-        /* copies the data (but *not* the shapekeys). */
-        Mesh *mesh = object_for_eval.data;
-        BKE_id_copy_ex(bmain, &mesh->id, (ID **)&tmpmesh, 0);
-        /* XXX BKE_mesh_copy() already handles materials usercount. */
-        do_mat_id_data_us = false;
-      }
-      /* if not getting the original caged mesh, get final derived mesh */
-      else {
-        /* Make a dummy mesh, saves copying */
-        Mesh *me_eval;
-        CustomData_MeshMasks mask = CD_MASK_MESH; /* this seems more suitable, exporter,
-                                                   * for example, needs CD_MASK_MDEFORMVERT */
+  /* Reset pointers before conversion. */
+  temp_curve->editfont = NULL;
+  temp_curve->editnurb = NULL;
+
+  /* Convert to mesh. */
+  BKE_mesh_from_nurbs_displist(bmain,
+                               temp_object,
+                               &temp_object->runtime.curve_cache->disp,
+                               uv_from_orco,
+                               curve->id.name + 2,
+                               true);
+
+  /* BKE_mesh_from_nurbs changes the type to a mesh, check it worked. If it didn't the curve did
+   * not have any segments or otherwise would have generated an empty mesh. */
+  if (temp_object->type != OB_MESH) {
+    BKE_id_free(NULL, temp_object);
+    return NULL;
+  }
 
-        if (calc_undeformed) {
-          mask.vmask |= CD_MASK_ORCO;
-        }
+  Mesh *mesh_result = temp_object->data;
 
-        if (render) {
-          me_eval = mesh_create_eval_final_render(depsgraph, sce, &object_for_eval, &mask);
-        }
-        else {
-          me_eval = mesh_create_eval_final_view(depsgraph, sce, &object_for_eval, &mask);
-        }
+  BKE_id_free(NULL, temp_object);
 
-        tmpmesh = BKE_mesh_add(bmain, ((ID *)object_for_eval.data)->name + 2);
-        BKE_mesh_nomain_to_mesh(me_eval, tmpmesh, &object_for_eval, &mask, true);
+  /* NOTE: Materials are copied in BKE_mesh_from_nurbs_displist(). */
 
-        /* Copy autosmooth settings from original mesh. */
-        Mesh *me = (Mesh *)object_for_eval.data;
-        tmpmesh->flag |= (me->flag & ME_AUTOSMOOTH);
-        tmpmesh->smoothresh = me->smoothresh;
-      }
+  return mesh_result;
+}
 
-      /* BKE_mesh_add/copy gives us a user count we don't need */
-      id_us_min(&tmpmesh->id);
+static Mesh *mesh_new_from_mball_object(Main *bmain, Object *object)
+{
+  MetaBall *mball = (MetaBall *)object->data;
 
-      break;
-    default:
-      /* "Object does not have geometry data") */
-      return NULL;
+  /* NOTE: We can only create mesh for a polygonized meta ball. This figures out all original meta
+   * balls and all evaluated child meta balls (since polygonization is only stored in the mother
+   * ball).
+   *
+   * We create empty mesh so scripters don't run into None objects. */
+  if (!DEG_is_evaluated_object(object) || object->runtime.curve_cache == NULL ||
+      BLI_listbase_is_empty(&object->runtime.curve_cache->disp)) {
+    return BKE_mesh_add(bmain, mball->id.name + 2);
   }
 
-  /* Copy materials to new mesh */
-  switch (object_for_eval.type) {
-    case OB_SURF:
-    case OB_FONT:
-    case OB_CURVE:
-      tmpmesh->totcol = tmpcu->totcol;
-
-      /* free old material list (if it exists) and adjust user counts */
-      if (tmpcu->mat) {
-        for (i = tmpcu->totcol; i-- > 0;) {
-          /* are we an object material or data based? */
-          tmpmesh->mat[i] = give_current_material(object_input, i + 1);
+  Mesh *mesh_result = BKE_mesh_add(bmain, ((ID *)object->data)->name + 2);
+  BKE_mesh_from_metaball(&object->runtime.curve_cache->disp, mesh_result);
 
-          if (((object_for_eval.matbits && object_for_eval.matbits[i]) || do_mat_id_data_us) &&
-              tmpmesh->mat[i]) {
-            id_us_plus(&tmpmesh->mat[i]->id);
-          }
-        }
-      }
-      break;
+  /* Copy materials. */
+  mesh_result->totcol = mball->totcol;
+  mesh_result->mat = MEM_dupallocN(mball->mat);
+  if (mball->mat != NULL) {
+    for (int i = mball->totcol; i-- > 0;) {
+      mesh_result->mat[i] = give_current_material(object, i + 1);
+    }
+  }
 
-    case OB_MBALL: {
-      MetaBall *tmpmb = (MetaBall *)object_for_eval.data;
-      tmpmesh->mat = MEM_dupallocN(tmpmb->mat);
-      tmpmesh->totcol = tmpmb->totcol;
+  return mesh_result;
+}
 
-      /* free old material list (if it exists) and adjust user counts */
-      if (tmpmb->mat) {
-        for (i = tmpmb->totcol; i-- > 0;) {
-          /* are we an object material or data based? */
-          tmpmesh->mat[i] = give_current_material(object_input, i + 1);
+static Mesh *mesh_new_from_mesh_object(Main *bmain, Object *object)
+{
+  Mesh *mesh_input = object->data;
+  Mesh *mesh_result = NULL;
+  BKE_id_copy_ex(bmain, &mesh_input->id, (ID **)&mesh_result, 0);
+  /* NOTE: Materials should already be copied. */
+  return mesh_result;
+}
 
-          if (((object_for_eval.matbits && object_for_eval.matbits[i]) || do_mat_id_data_us) &&
-              tmpmesh->mat[i]) {
-            id_us_plus(&tmpmesh->mat[i]->id);
-          }
-        }
-      }
+Mesh *BKE_mesh_new_from_object(Main *bmain, Object *object)
+{
+  Mesh *new_mesh = NULL;
+  switch (object->type) {
+    case OB_FONT:
+    case OB_CURVE:
+    case OB_SURF:
+      new_mesh = mesh_new_from_curve_type_object(bmain, object);
+      break;
+    case OB_MBALL:
+      new_mesh = mesh_new_from_mball_object(bmain, object);
       break;
-    }
-
     case OB_MESH:
-      if (!cage) {
-        Mesh *origmesh = object_for_eval.data;
-        tmpmesh->flag = origmesh->flag;
-        tmpmesh->mat = MEM_dupallocN(origmesh->mat);
-        tmpmesh->totcol = origmesh->totcol;
-        tmpmesh->smoothresh = origmesh->smoothresh;
-        if (origmesh->mat) {
-          for (i = origmesh->totcol; i-- > 0;) {
-            /* are we an object material or data based? */
-            tmpmesh->mat[i] = give_current_material(object_input, i + 1);
-
-            if (((object_for_eval.matbits && object_for_eval.matbits[i]) || do_mat_id_data_us) &&
-                tmpmesh->mat[i]) {
-              id_us_plus(&tmpmesh->mat[i]->id);
-            }
-          }
-        }
-      }
+      new_mesh = mesh_new_from_mesh_object(bmain, object);
       break;
-  } /* end copy materials */
-
-  return tmpmesh;
+    default:
+      /* Object does not have geometry data. */
+      return NULL;
+  }
+  if (new_mesh == NULL) {
+    /* Happens in special cases like request of mesh for non-mother meta ball. */
+    return NULL;
+  }
+  /* The result must have 0 users, since it's just a mesh which is free-dangling in the main
+   * database. All the copy and allocation functions to manipulate new Mesh datablock are ensuring
+   * an user.
+   * Here we control that user management went the way it's expected, and cancel out the user. */
+  BLI_assert(new_mesh->id.us == 1);
+  id_us_min(&new_mesh->id);
+  return new_mesh;
 }
 
 static void add_shapekey_layers(Mesh *mesh_dest, Mesh *mesh_src)
index da95db9..4f26ae2 100644 (file)
@@ -705,10 +705,9 @@ static size_t initialize_internal_images(BakeImages *bake_images, ReportList *re
 }
 
 /* create new mesh with edit mode changes and modifiers applied */
-static Mesh *bake_mesh_new_from_object(Depsgraph *depsgraph, Main *bmain, Scene *scene, Object *ob)
+static Mesh *bake_mesh_new_from_object(Main *bmain, Object *object)
 {
-  bool apply_modifiers = (ob->type != OB_MESH);
-  Mesh *me = BKE_mesh_new_from_object(depsgraph, bmain, scene, ob, apply_modifiers, false);
+  Mesh *me = BKE_mesh_new_from_object(bmain, object);
 
   if (me->flag & ME_AUTOSMOOTH) {
     BKE_mesh_split_faces(me, true);
@@ -904,7 +903,7 @@ static int bake(Render *re,
   ob_low_eval = DEG_get_evaluated_object(depsgraph, ob_low);
 
   /* get the mesh as it arrives in the renderer */
-  me_low = bake_mesh_new_from_object(depsgraph, bmain, scene, ob_low_eval);
+  me_low = bake_mesh_new_from_object(bmain, ob_low_eval);
 
   /* populate the pixel array with the face data */
   if ((is_selected_to_active && (ob_cage == NULL) && is_cage) == false) {
@@ -918,7 +917,7 @@ static int bake(Render *re,
 
     /* prepare cage mesh */
     if (ob_cage) {
-      me_cage = bake_mesh_new_from_object(depsgraph, bmain, scene, ob_cage_eval);
+      me_cage = bake_mesh_new_from_object(bmain, ob_cage_eval);
       if ((me_low->totpoly != me_cage->totpoly) || (me_low->totloop != me_cage->totloop)) {
         BKE_report(reports,
                    RPT_ERROR,
@@ -947,7 +946,7 @@ static int bake(Render *re,
         md = md_next;
       }
 
-      me_cage = bake_mesh_new_from_object(depsgraph, bmain, scene, ob_low_eval);
+      me_cage = bake_mesh_new_from_object(bmain, ob_low_eval);
       RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_images, uv_layer);
     }
 
@@ -966,7 +965,7 @@ static int bake(Render *re,
       highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter);
       highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER;
       highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE | BASE_ENABLED_RENDER);
-      highpoly[i].me = bake_mesh_new_from_object(depsgraph, bmain, scene, highpoly[i].ob_eval);
+      highpoly[i].me = bake_mesh_new_from_object(bmain, highpoly[i].ob_eval);
 
       /* lowpoly to highpoly transformation matrix */
       copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat);
@@ -1089,7 +1088,7 @@ static int bake(Render *re,
           }
 
           /* Evaluate modifiers again. */
-          me_nores = BKE_mesh_new_from_object(depsgraph, bmain, scene, ob_low_eval, true, false);
+          me_nores = BKE_mesh_new_from_object(bmain, ob_low_eval);
           RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images, uv_layer);
 
           RE_bake_normal_world_to_tangent(pixel_array_low,
index 06a356d..ed553c9 100644 (file)
@@ -98,10 +98,7 @@ NodeGroup *BlenderFileLoader::Load()
       continue;
     }
 
-    bool apply_modifiers = false;
-    bool calc_undeformed = false;
-    Mesh *mesh = BKE_mesh_new_from_object(
-        _depsgraph, _re->main, _re->scene, ob, apply_modifiers, calc_undeformed);
+    Mesh *mesh = BKE_mesh_new_from_object(_re->main, ob);
 
     if (mesh) {
       insertShapeNode(ob, mesh, ++id);
index 149cd7c..97d3d03 100644 (file)
@@ -435,6 +435,11 @@ StructRNA *rna_PropertyGroup_refine(PointerRNA *ptr)
   return ptr->type;
 }
 
+static ID *rna_ID_evaluated_get(ID *id, struct Depsgraph *depsgraph)
+{
+  return DEG_get_evaluated_id(depsgraph, id);
+}
+
 static ID *rna_ID_copy(ID *id, Main *bmain)
 {
   ID *newid;
@@ -1446,6 +1451,15 @@ static void rna_def_ID(BlenderRNA *brna)
   RNA_def_property_pointer_funcs(prop, "rna_IDPreview_get", NULL, NULL, NULL);
 
   /* functions */
+  func = RNA_def_function(srna, "evaluated_get", "rna_ID_evaluated_get");
+  RNA_def_function_ui_description(
+      func, "Get corresponding evaluated ID from the given dependency graph");
+  parm = RNA_def_pointer(
+      func, "depsgraph", "Depsgraph", "", "Dependency graph to perform lookup in");
+  RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+  parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID");
+  RNA_def_function_return(func, parm);
+
   func = RNA_def_function(srna, "copy", "rna_ID_copy");
   RNA_def_function_ui_description(
       func, "Create a copy of this data-block (not supported for all data-blocks)");
index 75f8b97..a2ac7cb 100644 (file)
@@ -135,12 +135,6 @@ static PointerRNA rna_Context_main_get(PointerRNA *ptr)
   return rna_pointer_inherit_refine(ptr, &RNA_BlendData, CTX_data_main(C));
 }
 
-static PointerRNA rna_Context_depsgraph_get(PointerRNA *ptr)
-{
-  bContext *C = (bContext *)ptr->data;
-  return rna_pointer_inherit_refine(ptr, &RNA_Depsgraph, CTX_data_depsgraph(C));
-}
-
 static PointerRNA rna_Context_scene_get(PointerRNA *ptr)
 {
   bContext *C = (bContext *)ptr->data;
@@ -204,6 +198,11 @@ static int rna_Context_mode_get(PointerRNA *ptr)
   return CTX_data_mode_enum(C);
 }
 
+static struct Depsgraph *rna_Context_evaluated_depsgraph_get(bContext *C)
+{
+  return CTX_data_evaluated_depsgraph(C);
+}
+
 #else
 
 void RNA_def_context(BlenderRNA *brna)
@@ -211,6 +210,9 @@ void RNA_def_context(BlenderRNA *brna)
   StructRNA *srna;
   PropertyRNA *prop;
 
+  FunctionRNA *func;
+  PropertyRNA *parm;
+
   srna = RNA_def_struct(brna, "Context", NULL);
   RNA_def_struct_ui_text(srna, "Context", "Current windowmanager and data context");
   RNA_def_struct_sdna(srna, "bContext");
@@ -267,11 +269,6 @@ void RNA_def_context(BlenderRNA *brna)
   RNA_def_property_struct_type(prop, "BlendData");
   RNA_def_property_pointer_funcs(prop, "rna_Context_main_get", NULL, NULL, NULL);
 
-  prop = RNA_def_property(srna, "depsgraph", PROP_POINTER, PROP_NONE);
-  RNA_def_property_clear_flag(prop, PROP_EDITABLE);
-  RNA_def_property_struct_type(prop, "Depsgraph");
-  RNA_def_property_pointer_funcs(prop, "rna_Context_depsgraph_get", NULL, NULL, NULL);
-
   prop = RNA_def_property(srna, "scene", PROP_POINTER, PROP_NONE);
   RNA_def_property_clear_flag(prop, PROP_EDITABLE);
   RNA_def_property_struct_type(prop, "Scene");
@@ -310,6 +307,16 @@ void RNA_def_context(BlenderRNA *brna)
   RNA_def_property_enum_items(prop, rna_enum_context_mode_items);
   RNA_def_property_clear_flag(prop, PROP_EDITABLE);
   RNA_def_property_enum_funcs(prop, "rna_Context_mode_get", NULL, NULL);
+
+  func = RNA_def_function(srna, "evaluated_depsgraph_get", "rna_Context_evaluated_depsgraph_get");
+  RNA_def_function_ui_description(
+      func,
+      "Get the dependency graph for the current scene and view layer, to access to data-blocks "
+      "with animation and modifiers applied. If any data-blocks have been edited, the dependency "
+      "graph will be updated. This invalidates all references to evaluated data-blocks from the "
+      "dependency graph.");
+  parm = RNA_def_pointer(func, "depsgraph", "Depsgraph", "", "Evaluated dependency graph");
+  RNA_def_function_return(func, parm);
 }
 
 #endif
index 447318d..08f37de 100644 (file)
@@ -41,6 +41,7 @@
 
 #  include "BKE_anim.h"
 #  include "BKE_object.h"
+#  include "BKE_scene.h"
 
 #  include "DEG_depsgraph_build.h"
 #  include "DEG_depsgraph_debug.h"
@@ -256,6 +257,11 @@ static void rna_Depsgraph_debug_stats(Depsgraph *depsgraph, char *result)
                outer);
 }
 
+static void rna_Depsgraph_update(Depsgraph *depsgraph, Main *bmain)
+{
+  BKE_scene_graph_update_tagged(depsgraph, bmain);
+}
+
 /* Iteration over objects, simple version */
 
 static void rna_Depsgraph_objects_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
@@ -636,6 +642,15 @@ static void rna_def_depsgraph(BlenderRNA *brna)
   RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0); /* needed for string return value */
   RNA_def_function_output(func, parm);
 
+  /* Updates. */
+
+  func = RNA_def_function(srna, "update", "rna_Depsgraph_update");
+  RNA_def_function_ui_description(
+      func,
+      "Re-evaluate any modified data-blocks, for example for animation or modifiers. "
+      "This invalidates all references to evaluated data-blocks from this dependency graph.");
+  RNA_def_function_flag(func, FUNC_USE_MAIN);
+
   /* Queries for original datablockls (the ones depsgraph is built for). */
 
   prop = RNA_def_property(srna, "scene", PROP_POINTER, PROP_NONE);
index fc0950c..d93f8c4 100644 (file)
@@ -550,10 +550,7 @@ int rna_parameter_size(struct PropertyRNA *parm);
 
 struct Mesh *rna_Main_meshes_new_from_object(struct Main *bmain,
                                              struct ReportList *reports,
-                                             struct Depsgraph *depsgraph,
-                                             struct Object *ob,
-                                             bool apply_modifiers,
-                                             bool calc_undeformed);
+                                             struct Object *object);
 
 /* XXX, these should not need to be defined here~! */
 struct MTex *rna_mtex_texture_slots_add(struct ID *self,
index b21cd33..8f48738 100644 (file)
@@ -317,16 +317,9 @@ static Mesh *rna_Main_meshes_new(Main *bmain, const char *name)
 }
 
 /* copied from Mesh_getFromObject and adapted to RNA interface */
-Mesh *rna_Main_meshes_new_from_object(Main *bmain,
-                                      ReportList *reports,
-                                      Depsgraph *depsgraph,
-                                      Object *ob,
-                                      bool apply_modifiers,
-                                      bool calc_undeformed)
+Mesh *rna_Main_meshes_new_from_object(Main *bmain, ReportList *reports, Object *object)
 {
-  Scene *sce = DEG_get_evaluated_scene(depsgraph);
-
-  switch (ob->type) {
+  switch (object->type) {
     case OB_FONT:
     case OB_CURVE:
     case OB_SURF:
@@ -338,7 +331,7 @@ Mesh *rna_Main_meshes_new_from_object(Main *bmain,
       return NULL;
   }
 
-  return BKE_mesh_new_from_object(depsgraph, bmain, sce, ob, apply_modifiers, calc_undeformed);
+  return BKE_mesh_new_from_object(bmain, object);
 }
 
 static Light *rna_Main_lights_new(Main *bmain, const char *name, int type)
@@ -951,24 +944,13 @@ void RNA_def_main_meshes(BlenderRNA *brna, PropertyRNA *cprop)
   RNA_def_function_return(func, parm);
 
   func = RNA_def_function(srna, "new_from_object", "rna_Main_meshes_new_from_object");
-  RNA_def_function_ui_description(func,
-                                  "Add a new mesh created from object with modifiers applied");
+  RNA_def_function_ui_description(
+      func,
+      "Add a new mesh created from given object (undeformed geometry if object is original, and "
+      "final evaluated geometry, with all modifiers etc., if object is evaluated)");
   RNA_def_function_flag(func, FUNC_USE_REPORTS);
-  parm = RNA_def_pointer(func,
-                         "depsgraph",
-                         "Depsgraph",
-                         "Dependency Graph",
-                         "Evaluated dependency graph within which to evaluate modifiers");
-  RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
   parm = RNA_def_pointer(func, "object", "Object", "", "Object to create mesh from");
   RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
-  parm = RNA_def_boolean(func, "apply_modifiers", 0, "", "Apply modifiers");
-  RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
-  RNA_def_boolean(func,
-                  "calc_undeformed",
-                  false,
-                  "Calculate Undeformed",
-                  "Calculate undeformed vertex coordinates");
   parm = RNA_def_pointer(func,
                          "mesh",
                          "Mesh",
index c997a82..d94abd4 100644 (file)
@@ -375,18 +375,11 @@ static void rna_Object_camera_fit_coords(
 }
 
 /* copied from Mesh_getFromObject and adapted to RNA interface */
-/* settings: 0 - preview, 1 - render */
-static Mesh *rna_Object_to_mesh(Object *ob,
-                                bContext *C,
-                                ReportList *reports,
-                                Depsgraph *depsgraph,
-                                bool apply_modifiers,
-                                bool calc_undeformed)
+static Mesh *rna_Object_to_mesh(Object *object, bContext *C, ReportList *reports)
 {
   Main *bmain = CTX_data_main(C);
 
-  return rna_Main_meshes_new_from_object(
-      bmain, reports, depsgraph, ob, apply_modifiers, calc_undeformed);
+  return rna_Main_meshes_new_from_object(bmain, reports, object);
 }
 
 static PointerRNA rna_Object_shape_key_add(
@@ -882,21 +875,9 @@ void RNA_api_object(StructRNA *srna)
 
   /* mesh */
   func = RNA_def_function(srna, "to_mesh", "rna_Object_to_mesh");
-  RNA_def_function_ui_description(func, "Create a Mesh data-block with modifiers applied");
+  RNA_def_function_ui_description(func,
+                                  "Create a Mesh data-block from the current state of the object");
   RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_CONTEXT);
-  parm = RNA_def_pointer(func,
-                         "depsgraph",
-                         "Depsgraph",
-                         "Dependency Graph",
-                         "Evaluated dependency graph within which to evaluate modifiers");
-  RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
-  parm = RNA_def_boolean(func, "apply_modifiers", 0, "", "Apply modifiers");
-  RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
-  RNA_def_boolean(func,
-                  "calc_undeformed",
-                  false,
-                  "Calculate Undeformed",
-                  "Calculate undeformed vertex coordinates");
   parm = RNA_def_pointer(func,
                          "mesh",
                          "Mesh",
index e29891b..1d4d2e9 100644 (file)
@@ -116,23 +116,6 @@ static void rna_Scene_uvedit_aspect(Scene *scene, Object *ob, float *aspect)
   aspect[0] = aspect[1] = 1.0f;
 }
 
-static void rna_Scene_update_tagged(Scene *scene, Main *bmain)
-{
-#  ifdef WITH_PYTHON
-  BPy_BEGIN_ALLOW_THREADS;
-#  endif
-
-  for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL;
-       view_layer = view_layer->next) {
-    Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true);
-    BKE_scene_graph_update_tagged(depsgraph, bmain);
-  }
-
-#  ifdef WITH_PYTHON
-  BPy_END_ALLOW_THREADS;
-#  endif
-}
-
 static void rna_SceneRender_get_frame_path(
     RenderData *rd, Main *bmain, int frame, bool preview, const char *view, char *name)
 {
@@ -303,11 +286,6 @@ void RNA_api_scene(StructRNA *srna)
       func, "subframe", 0.0, 0.0, 1.0, "", "Sub-frame time, between 0.0 and 1.0", 0.0, 1.0);
   RNA_def_function_flag(func, FUNC_USE_MAIN);
 
-  func = RNA_def_function(srna, "update", "rna_Scene_update_tagged");
-  RNA_def_function_ui_description(
-      func, "Update data tagged to be updated from previous access to data or operators");
-  RNA_def_function_flag(func, FUNC_USE_MAIN);
-
   func = RNA_def_function(srna, "uvedit_aspect", "rna_Scene_uvedit_aspect");
   RNA_def_function_ui_description(func, "Get uv aspect for current object");
   parm = RNA_def_pointer(func, "object", "Object", "", "Object");
index 2cde6d5..6362427 100644 (file)
@@ -92,8 +92,10 @@ class SimpleImportTest(AbstractAlembicTest):
         # translates to "no parent" in Blender.
         self.assertIsNone(objects['locator2'].parent)
 
+        depsgraph = bpy.context.evaluated_depsgraph_get()
+
         # Shouldn't have inherited the ABC parent's transform.
-        loc2 = bpy.context.depsgraph.id_eval_get(objects['locator2'])
+        loc2 = depsgraph.id_eval_get(objects['locator2'])
         x, y, z = objects['locator2'].matrix_world.to_translation()
         self.assertAlmostEqual(0, x)
         self.assertAlmostEqual(0, y)
@@ -103,7 +105,7 @@ class SimpleImportTest(AbstractAlembicTest):
         self.assertEqual(objects['locator2'], objects['locatorShape2'].parent)
 
         # Should have inherited its ABC parent's transform.
-        locshp2 = bpy.context.depsgraph.id_eval_get(objects['locatorShape2'])
+        locshp2 = depsgraph.id_eval_get(objects['locatorShape2'])
         x, y, z = locshp2.matrix_world.to_translation()
         self.assertAlmostEqual(0, x)
         self.assertAlmostEqual(0, y)
@@ -142,9 +144,11 @@ class SimpleImportTest(AbstractAlembicTest):
         self.assertEqual({'FINISHED'}, res)
         cube = bpy.context.active_object
 
+        depsgraph = bpy.context.evaluated_depsgraph_get()
+
         # Check that the file loaded ok.
         bpy.context.scene.frame_set(10)
-        cube = bpy.context.depsgraph.id_eval_get(cube)
+        cube = depsgraph.id_eval_get(cube)
         x, y, z = cube.matrix_world.to_euler('XYZ')
         self.assertAlmostEqual(x, 0)
         self.assertAlmostEqual(y, 0)
@@ -155,7 +159,7 @@ class SimpleImportTest(AbstractAlembicTest):
         bpy.data.cache_files[fname].filepath = relpath
         bpy.context.scene.frame_set(10)
 
-        cube = bpy.context.depsgraph.id_eval_get(cube)
+        cube = depsgraph.id_eval_get(cube)
         x, y, z = cube.matrix_world.to_euler('XYZ')
         self.assertAlmostEqual(x, 0)
         self.assertAlmostEqual(y, 0)
@@ -163,9 +167,9 @@ class SimpleImportTest(AbstractAlembicTest):
 
         # Replace the Alembic file; this should apply new animation.
         bpy.data.cache_files[fname].filepath = relpath.replace('1.abc', '2.abc')
-        bpy.context.scene.update()
+        depsgraph.update()
 
-        cube = bpy.context.depsgraph.id_eval_get(cube)
+        cube = depsgraph.id_eval_get(cube)
         x, y, z = cube.matrix_world.to_euler('XYZ')
         self.assertAlmostEqual(x, math.pi / 2, places=5)
         self.assertAlmostEqual(y, 0)
@@ -180,10 +184,13 @@ class SimpleImportTest(AbstractAlembicTest):
         self.assertEqual({'FINISHED'}, res)
         plane = bpy.context.active_object
 
+        depsgraph = bpy.context.evaluated_depsgraph_get()
+
         # Check that the file loaded ok.
         bpy.context.scene.frame_set(6)
         scene = bpy.context.scene
-        mesh = plane.to_mesh(bpy.context.depsgraph, apply_modifiers=True, calc_undeformed=False)
+        plane_eval = plane.evaluated_get(depsgraph)
+        mesh = plane_eval.to_mesh()
         self.assertAlmostEqual(-1, mesh.vertices[0].co.x)
         self.assertAlmostEqual(-1, mesh.vertices[0].co.y)
         self.assertAlmostEqual(0.5905638933181763, mesh.vertices[0].co.z)
@@ -193,7 +200,8 @@ class SimpleImportTest(AbstractAlembicTest):
         bpy.data.cache_files[fname].filepath = relpath
         scene.frame_set(6)
 
-        mesh = plane.to_mesh(bpy.context.depsgraph, apply_modifiers=True, calc_undeformed=False)
+        plane_eval = plane.evaluated_get(depsgraph)
+        mesh = plane_eval.to_mesh()
         self.assertAlmostEqual(1, mesh.vertices[3].co.x)
         self.assertAlmostEqual(1, mesh.vertices[3].co.y)
         self.assertAlmostEqual(0.5905638933181763, mesh.vertices[3].co.z)