Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/python_api/examples/bpy.types.Depsgraph.4.py10
-rw-r--r--doc/python_api/examples/bpy.types.Depsgraph.5.py78
-rw-r--r--doc/python_api/examples/bpy.types.Depsgraph.6.py61
-rw-r--r--intern/cycles/blender/blender_util.h12
-rw-r--r--source/blender/blenkernel/BKE_mesh.h8
-rw-r--r--source/blender/blenkernel/BKE_object.h9
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c114
-rw-r--r--source/blender/blenkernel/intern/object.c19
-rw-r--r--source/blender/editors/object/object_bake_api.c22
-rw-r--r--source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp5
-rw-r--r--source/blender/makesdna/DNA_object_types.h5
-rw-r--r--source/blender/makesrna/intern/rna_internal.h3
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c4
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c40
-rw-r--r--tests/python/bl_alembic_import_test.py2
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.