diff options
-rw-r--r-- | doc/python_api/examples/bpy.types.Depsgraph.4.py | 10 | ||||
-rw-r--r-- | doc/python_api/examples/bpy.types.Depsgraph.5.py | 78 | ||||
-rw-r--r-- | doc/python_api/examples/bpy.types.Depsgraph.6.py | 61 | ||||
-rw-r--r-- | intern/cycles/blender/blender_util.h | 12 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_mesh.h | 8 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_object.h | 9 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/mesh_convert.c | 114 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object.c | 19 | ||||
-rw-r--r-- | source/blender/editors/object/object_bake_api.c | 22 | ||||
-rw-r--r-- | source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp | 5 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_object_types.h | 5 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_internal.h | 3 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_main_api.c | 4 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_object_api.c | 40 | ||||
-rw-r--r-- | tests/python/bl_alembic_import_test.py | 2 |
15 files changed, 283 insertions, 109 deletions
diff --git a/doc/python_api/examples/bpy.types.Depsgraph.4.py b/doc/python_api/examples/bpy.types.Depsgraph.4.py index 5c7b76edab6..41f94a32c4c 100644 --- a/doc/python_api/examples/bpy.types.Depsgraph.4.py +++ b/doc/python_api/examples/bpy.types.Depsgraph.4.py @@ -2,8 +2,8 @@ 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. +Object.to_mesh() is closely interacting with dependency graph: its behavior depends on whether it +is 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: @@ -14,7 +14,7 @@ animation or modifiers into account: When is used on evaluated object all modifiers are taken into account. -.. note:: The result mesh is added to the main database. +.. note:: The result mesh is owned by the object. It can be freed by calling `object.to_mesh_clear()`. .. note:: If object does not have geometry (i.e. camera) the functions returns None. """ import bpy @@ -40,13 +40,13 @@ class OBJECT_OT_object_to_mesh(bpy.types.Operator): 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) + object.to_mesh_clear() # 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) + object_eval.to_mesh_clear() return {'FINISHED'} diff --git a/doc/python_api/examples/bpy.types.Depsgraph.5.py b/doc/python_api/examples/bpy.types.Depsgraph.5.py index 781d0202931..a37d2674ecd 100644 --- a/doc/python_api/examples/bpy.types.Depsgraph.5.py +++ b/doc/python_api/examples/bpy.types.Depsgraph.5.py @@ -1,60 +1,56 @@ """ -Dependency graph: Simple exporter -+++++++++++++++++++++++++++++++++ +Dependency graph: bpy.data.meshes.new_from_object() ++++++++++++++++++++++++++++++++++++++++++++++++++++ -This example is a combination of all previous ones, and shows how to write a simple exporter -script. +Object.to_mesh() is closely interacting with dependency graph: its behavior depends on whether it +is 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. + +All the references (such as materials) are re-mapped to original. This ensures validity and +consistency of the main database. + +.. 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_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") +class OBJECT_OT_mesh_from_object(bpy.types.Operator): + """Convert selected object to mesh and show number of vertices""" + bl_label = "DEG Mesh From Object" + bl_idname = "object.mesh_from_object" 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() - 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) - + object_eval = object.evaluated_get(depsgraph) + mesh_from_eval = bpy.data.meshes.new_from_object(object_eval) + self.report({'INFO'}, f"{len(mesh_from_eval.vertices)} in new mesh, and is ready for use!") 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) + bpy.utils.register_class(OBJECT_OT_mesh_from_object) def unregister(): - bpy.utils.unregister_class(OBJECT_OT_simple_exporter) + bpy.utils.unregister_class(OBJECT_OT_mesh_from_object) if __name__ == "__main__": diff --git a/doc/python_api/examples/bpy.types.Depsgraph.6.py b/doc/python_api/examples/bpy.types.Depsgraph.6.py new file mode 100644 index 00000000000..781d0202931 --- /dev/null +++ b/doc/python_api/examples/bpy.types.Depsgraph.6.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() diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 2a964d0e4d0..972d7296727 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -43,7 +43,7 @@ CCL_NAMESPACE_BEGIN void python_thread_state_save(void **python_thread_state); void python_thread_state_restore(void **python_thread_state); -static inline BL::Mesh object_to_mesh(BL::BlendData &data, +static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/, BL::Object &object, BL::Depsgraph & /*depsgraph*/, bool /*calc_undeformed*/, @@ -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(object); + mesh = object.to_mesh(); } } else { - mesh = data.meshes.new_from_object(object); + mesh = object.to_mesh(); } #if 0 @@ -102,11 +102,13 @@ static inline BL::Mesh object_to_mesh(BL::BlendData &data, return mesh; } -static inline void free_object_to_mesh(BL::BlendData &data, BL::Object &object, BL::Mesh &mesh) +static inline void free_object_to_mesh(BL::BlendData & /*data*/, + BL::Object &object, + BL::Mesh &mesh) { /* Free mesh if we didn't just use the existing one. */ if (object.data().ptr.data != mesh.ptr.data) { - data.meshes.remove(mesh, false, true, false); + object.to_mesh_clear(); } } diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 8ea54457f38..c410946f438 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -208,7 +208,13 @@ 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 Main *bmain, struct Object *object); +/* Create new mesh from the given object at its current state. + * The owner of this mesh is unknown, it is up to the caller to decide. */ +struct Mesh *BKE_mesh_new_from_object(struct Object *object); + +/* This is a version of BKE_mesh_new_from_object() which stores mesh in the given main database. */ +struct Mesh *BKE_mesh_new_from_object_to_bmain(struct Main *bmain, struct Object *object); + struct Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob_eval, diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index c373fbfe478..aa4d9696527 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -396,6 +396,15 @@ bool BKE_object_empty_image_frame_is_visible_in_view3d(const struct Object *ob, bool BKE_object_empty_image_data_is_visible_in_view3d(const struct Object *ob, const struct RegionView3D *rv3d); +/* This is an utility function for Python's object.to_mesh() (the naming is not very clear though). + * The result is owned by the object. + * + * The mesh will be freed when object is re-evaluated or is destroyed. It is possible to force to + * clear memory sued by this mesh by calling BKE_object_to_mesh_clear(). */ +struct Mesh *BKE_object_to_mesh(struct Object *object); + +void BKE_object_to_mesh_clear(struct Object *object); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index a5c97cd1182..6d0245cbc88 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -38,6 +38,7 @@ #include "BKE_main.h" #include "BKE_DerivedMesh.h" #include "BKE_key.h" +#include "BKE_library_query.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" @@ -612,7 +613,13 @@ void BKE_mesh_from_nurbs_displist(Main *bmain, } /* make mesh */ - me = BKE_mesh_add(bmain, obdata_name); + if (bmain != NULL) { + me = BKE_mesh_add(bmain, obdata_name); + } + else { + me = BKE_id_new_nomain(ID_ME, obdata_name); + } + me->totvert = totvert; me->totedge = totedge; me->totloop = totloop; @@ -632,7 +639,13 @@ void BKE_mesh_from_nurbs_displist(Main *bmain, BKE_mesh_calc_normals(me); } else { - me = BKE_mesh_add(bmain, obdata_name); + if (bmain != NULL) { + me = BKE_mesh_add(bmain, obdata_name); + } + else { + me = BKE_id_new_nomain(ID_ME, obdata_name); + } + ob->runtime.mesh_eval = NULL; BKE_mesh_nomain_to_mesh(me_eval, me, ob, &CD_MASK_MESH, true); } @@ -662,16 +675,18 @@ void BKE_mesh_from_nurbs_displist(Main *bmain, ob->type = OB_MESH; /* other users */ - ob1 = bmain->objects.first; - while (ob1) { - if (ob1->data == cu) { - ob1->type = OB_MESH; - - id_us_min((ID *)ob1->data); - ob1->data = ob->data; - id_us_plus((ID *)ob1->data); + if (bmain != NULL) { + ob1 = bmain->objects.first; + while (ob1) { + if (ob1->data == cu) { + ob1->type = OB_MESH; + + id_us_min((ID *)ob1->data); + ob1->data = ob->data; + id_us_plus((ID *)ob1->data); + } + ob1 = ob1->id.next; } - ob1 = ob1->id.next; } if (temporary) { @@ -995,7 +1010,7 @@ static void curve_to_mesh_eval_ensure(Object *object) BKE_object_free_curve_cache(&taper_object); } -static Mesh *mesh_new_from_curve_type_object(Main *bmain, Object *object) +static Mesh *mesh_new_from_curve_type_object(Object *object) { Curve *curve = object->data; const bool uv_from_orco = (curve->flag & CU_UV_ORCO) != 0; @@ -1014,7 +1029,7 @@ static Mesh *mesh_new_from_curve_type_object(Main *bmain, Object *object) temp_curve->editnurb = NULL; /* Convert to mesh. */ - BKE_mesh_from_nurbs_displist(bmain, + BKE_mesh_from_nurbs_displist(NULL, temp_object, &temp_object->runtime.curve_cache->disp, uv_from_orco, @@ -1037,7 +1052,7 @@ static Mesh *mesh_new_from_curve_type_object(Main *bmain, Object *object) return mesh_result; } -static Mesh *mesh_new_from_mball_object(Main *bmain, Object *object) +static Mesh *mesh_new_from_mball_object(Object *object) { MetaBall *mball = (MetaBall *)object->data; @@ -1048,10 +1063,10 @@ static Mesh *mesh_new_from_mball_object(Main *bmain, Object *object) * 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); + return BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2); } - Mesh *mesh_result = BKE_mesh_add(bmain, ((ID *)object->data)->name + 2); + Mesh *mesh_result = BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2); BKE_mesh_from_metaball(&object->runtime.curve_cache->disp, mesh_result); /* Copy materials. */ @@ -1066,29 +1081,32 @@ static Mesh *mesh_new_from_mball_object(Main *bmain, Object *object) return mesh_result; } -static Mesh *mesh_new_from_mesh_object(Main *bmain, Object *object) +static Mesh *mesh_new_from_mesh_object(Object *object) { Mesh *mesh_input = object->data; Mesh *mesh_result = NULL; - BKE_id_copy_ex(bmain, &mesh_input->id, (ID **)&mesh_result, 0); + BKE_id_copy_ex(NULL, + &mesh_input->id, + (ID **)&mesh_result, + LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT); /* NOTE: Materials should already be copied. */ return mesh_result; } -Mesh *BKE_mesh_new_from_object(Main *bmain, Object *object) +Mesh *BKE_mesh_new_from_object(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); + new_mesh = mesh_new_from_curve_type_object(object); break; case OB_MBALL: - new_mesh = mesh_new_from_mball_object(bmain, object); + new_mesh = mesh_new_from_mball_object(object); break; case OB_MESH: - new_mesh = mesh_new_from_mesh_object(bmain, object); + new_mesh = mesh_new_from_mesh_object(object); break; default: /* Object does not have geometry data. */ @@ -1098,15 +1116,55 @@ Mesh *BKE_mesh_new_from_object(Main *bmain, Object *object) /* 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); + /* The result must have 0 users, since it's just a mesh which is free-dangling data-block. + * All the conversion functions are supposed to ensure mesh is not counted. */ + BLI_assert(new_mesh->id.us == 0); return new_mesh; } +static int foreach_libblock_make_original_and_usercount_callback(void *user_data_v, + ID *id_self, + ID **id_p, + int cb_flag) +{ + UNUSED_VARS(user_data_v, id_self, cb_flag); + if (*id_p == NULL) { + return IDWALK_RET_NOP; + } + *id_p = DEG_get_original_id(*id_p); + id_us_plus(*id_p); + return IDWALK_RET_NOP; +} + +Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, Object *object) +{ + Mesh *mesh = BKE_mesh_new_from_object(object); + + /* Make sure mesh only points original datablocks, also increase users of materials and other + * possibly referenced data-blocks. + * + * Going to original data-blocks is required to have bmain in a consistent state, where + * everything is only allowed to reference original data-blocks. + * + * user-count is required is because so far mesh was in a limbo, where library management does + * not perform any user management (i.e. copy of a mesh will not increase users of materials). */ + BKE_library_foreach_ID_link( + NULL, &mesh->id, foreach_libblock_make_original_and_usercount_callback, NULL, IDWALK_NOP); + + /* Append the mesh to bmain. + * We do it a bit longer way since there is no simple and clear way of adding existing datablock + * to the bmain. So we allocate new empty mesh in the bmain (which guarantess all the naming and + * orders and flags) and move the temporary mesh in place there. */ + Mesh *mesh_in_bmain = BKE_mesh_add(bmain, mesh->id.name + 2); + BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, NULL, &CD_MASK_MESH, true); + + /* Make sure user count from BKE_mesh_add() is the one we expect here and bring it down to 0. */ + BLI_assert(mesh_in_bmain->id.us == 1); + id_us_min(&mesh_in_bmain->id); + + return mesh_in_bmain; +} + static void add_shapekey_layers(Mesh *mesh_dest, Mesh *mesh_src) { KeyBlock *kb; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 78f2b10305e..95be7f8f51f 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -469,6 +469,7 @@ void BKE_object_free_derived_caches(Object *ob) ob->runtime.mesh_deform_eval = NULL; } + BKE_object_to_mesh_clear(ob); BKE_object_free_curve_cache(ob); /* clear grease pencil data */ @@ -4497,3 +4498,21 @@ void BKE_object_update_select_id(struct Main *bmain) ob = ob->id.next; } } + +Mesh *BKE_object_to_mesh(Object *object) +{ + BKE_object_to_mesh_clear(object); + + Mesh *mesh = BKE_mesh_new_from_object(object); + object->runtime.object_as_temp_mesh = mesh; + return mesh; +} + +void BKE_object_to_mesh_clear(Object *object) +{ + if (object->runtime.object_as_temp_mesh == NULL) { + return; + } + BKE_id_free(NULL, object->runtime.object_as_temp_mesh); + object->runtime.object_as_temp_mesh = NULL; +} diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 4f26ae27d9c..f87342a14ad 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -705,9 +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(Main *bmain, Object *object) +static Mesh *bake_mesh_new_from_object(Object *object) { - Mesh *me = BKE_mesh_new_from_object(bmain, object); + Mesh *me = BKE_object_to_mesh(object); if (me->flag & ME_AUTOSMOOTH) { BKE_mesh_split_faces(me, true); @@ -903,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(bmain, ob_low_eval); + me_low = bake_mesh_new_from_object(ob_low_eval); /* populate the pixel array with the face data */ if ((is_selected_to_active && (ob_cage == NULL) && is_cage) == false) { @@ -917,7 +917,7 @@ static int bake(Render *re, /* prepare cage mesh */ if (ob_cage) { - me_cage = bake_mesh_new_from_object(bmain, ob_cage_eval); + me_cage = bake_mesh_new_from_object(ob_cage_eval); if ((me_low->totpoly != me_cage->totpoly) || (me_low->totloop != me_cage->totloop)) { BKE_report(reports, RPT_ERROR, @@ -946,7 +946,7 @@ static int bake(Render *re, md = md_next; } - me_cage = bake_mesh_new_from_object(bmain, ob_low_eval); + me_cage = BKE_object_to_mesh(ob_low_eval); RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_images, uv_layer); } @@ -965,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(bmain, highpoly[i].ob_eval); + highpoly[i].me = BKE_object_to_mesh(highpoly[i].ob_eval); /* lowpoly to highpoly transformation matrix */ copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat); @@ -1088,7 +1088,7 @@ static int bake(Render *re, } /* Evaluate modifiers again. */ - me_nores = BKE_mesh_new_from_object(bmain, ob_low_eval); + me_nores = BKE_object_to_mesh(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, @@ -1098,7 +1098,7 @@ static int bake(Render *re, me_nores, normal_swizzle, ob_low_eval->obmat); - BKE_id_free(bmain, me_nores); + BKE_object_to_mesh_clear(ob_low_eval); if (md) { md->mode = mode; @@ -1222,7 +1222,7 @@ cleanup: int i; for (i = 0; i < tot_highpoly; i++) { if (highpoly[i].me) { - BKE_id_free(bmain, highpoly[i].me); + BKE_object_to_mesh_clear(highpoly[i].ob_eval); } } MEM_freeN(highpoly); @@ -1253,11 +1253,11 @@ cleanup: } if (me_low) { - BKE_id_free(bmain, me_low); + BKE_object_to_mesh_clear(ob_low_eval); } if (me_cage) { - BKE_id_free(bmain, me_cage); + BKE_object_to_mesh_clear(ob_cage_eval); } DEG_graph_free(depsgraph); diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp index ed553c9583c..ee13583e67a 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp @@ -23,6 +23,7 @@ #include "BLI_utildefines.h" #include "BKE_global.h" +#include "BKE_object.h" #include <sstream> @@ -98,11 +99,11 @@ NodeGroup *BlenderFileLoader::Load() continue; } - Mesh *mesh = BKE_mesh_new_from_object(_re->main, ob); + Mesh *mesh = BKE_object_to_mesh(ob); if (mesh) { insertShapeNode(ob, mesh, ++id); - BKE_id_free_ex(_re->main, &mesh->id, LIB_ID_FREE_NO_UI_USER, true); + BKE_object_to_mesh_clear(ob); } } DEG_OBJECT_ITER_END; diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index a73c762e3bf..0b2f3d69bd2 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -43,6 +43,7 @@ struct DerivedMesh; struct FluidsimSettings; struct GpencilBatchCache; struct Ipo; +struct Mesh; struct Material; struct Object; struct PartDeflect; @@ -160,6 +161,10 @@ typedef struct Object_Runtime { */ struct Mesh *mesh_deform_eval; + /* This is a mesh representation of corresponding object. + * It created when Python calls `object.to_mesh()`. */ + struct Mesh *object_as_temp_mesh; + /** Runtime evaluated curve-specific data, not stored in the file. */ struct CurveCache *curve_cache; diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index d93f8c4414b..64024166c50 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -548,9 +548,6 @@ PointerRNA rna_pointer_inherit_refine(struct PointerRNA *ptr, struct StructRNA * int rna_parameter_size(struct PropertyRNA *parm); -struct Mesh *rna_Main_meshes_new_from_object(struct Main *bmain, - struct ReportList *reports, - struct Object *object); /* XXX, these should not need to be defined here~! */ struct MTex *rna_mtex_texture_slots_add(struct ID *self, diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 8f48738a27e..f6fee2580b8 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -317,7 +317,7 @@ 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, Object *object) +static Mesh *rna_Main_meshes_new_from_object(Main *bmain, ReportList *reports, Object *object) { switch (object->type) { case OB_FONT: @@ -331,7 +331,7 @@ Mesh *rna_Main_meshes_new_from_object(Main *bmain, ReportList *reports, Object * return NULL; } - return BKE_mesh_new_from_object(bmain, object); + return BKE_mesh_new_from_object_to_bmain(bmain, object); } static Light *rna_Main_lights_new(Main *bmain, const char *name, int type) diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index d94abd4066a..da00e1f155d 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -375,11 +375,28 @@ static void rna_Object_camera_fit_coords( } /* copied from Mesh_getFromObject and adapted to RNA interface */ -static Mesh *rna_Object_to_mesh(Object *object, bContext *C, ReportList *reports) +static Mesh *rna_Object_to_mesh(Object *object, ReportList *reports) { - Main *bmain = CTX_data_main(C); + /* TODO(sergey): Make it more re-usable function, de-duplicate with + * rna_Main_meshes_new_from_object. */ + switch (object->type) { + case OB_FONT: + case OB_CURVE: + case OB_SURF: + case OB_MBALL: + case OB_MESH: + break; + default: + BKE_report(reports, RPT_ERROR, "Object does not have geometry data"); + return NULL; + } + + return BKE_object_to_mesh(object); +} - return rna_Main_meshes_new_from_object(bmain, reports, object); +static void rna_Object_to_mesh_clear(Object *object) +{ + BKE_object_to_mesh_clear(object); } static PointerRNA rna_Object_shape_key_add( @@ -875,16 +892,17 @@ 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 from the current state of the object"); - RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_CONTEXT); - parm = RNA_def_pointer(func, - "mesh", - "Mesh", - "", - "Mesh created from object, remove it if it is only used for export"); + RNA_def_function_ui_description( + func, + "Create a Mesh data-block from the current state of the object. The object owns the " + "data-block. To force free it use to_mesh_clear()"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "mesh", "Mesh", "", "Mesh created from object"); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "to_mesh_clear", "rna_Object_to_mesh_clear"); + RNA_def_function_ui_description(func, "Clears mesh data-block created by to_mesh()"); + /* Armature */ func = RNA_def_function(srna, "find_armature", "modifiers_isDeformedByArmature"); RNA_def_function_ui_description( diff --git a/tests/python/bl_alembic_import_test.py b/tests/python/bl_alembic_import_test.py index 63624270bd8..baf76dc581f 100644 --- a/tests/python/bl_alembic_import_test.py +++ b/tests/python/bl_alembic_import_test.py @@ -194,6 +194,7 @@ class SimpleImportTest(AbstractAlembicTest): 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) + plane_eval.to_mesh_clear() # Change path from absolute to relative. This should not break the animation. scene.frame_set(1) @@ -205,6 +206,7 @@ class SimpleImportTest(AbstractAlembicTest): 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) + plane_eval.to_mesh_clear() def test_import_long_names(self): # This file contains very long names. The longest name is 4047 chars. |