Changeset View
Standalone View
source/blender/blenkernel/intern/mesh_convert.cc
| Show All 35 Lines | |||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_string.h" | #include "BLI_string.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BKE_DerivedMesh.h" | #include "BKE_DerivedMesh.h" | ||||
| #include "BKE_deform.h" | #include "BKE_deform.h" | ||||
| #include "BKE_displist.h" | #include "BKE_displist.h" | ||||
| #include "BKE_editmesh.h" | #include "BKE_editmesh.h" | ||||
| #include "BKE_geometry_set.hh" | |||||
| #include "BKE_key.h" | #include "BKE_key.h" | ||||
| #include "BKE_lib_id.h" | #include "BKE_lib_id.h" | ||||
| #include "BKE_lib_query.h" | #include "BKE_lib_query.h" | ||||
| #include "BKE_main.h" | #include "BKE_main.h" | ||||
| #include "BKE_material.h" | #include "BKE_material.h" | ||||
| #include "BKE_mball.h" | #include "BKE_mball.h" | ||||
| #include "BKE_mesh.h" | #include "BKE_mesh.h" | ||||
| #include "BKE_mesh_runtime.h" | #include "BKE_mesh_runtime.h" | ||||
| #include "BKE_mesh_wrapper.h" | #include "BKE_mesh_wrapper.h" | ||||
| #include "BKE_modifier.h" | #include "BKE_modifier.h" | ||||
| #include "BKE_spline.hh" | |||||
| /* these 2 are only used by conversion functions */ | /* these 2 are only used by conversion functions */ | ||||
| #include "BKE_curve.h" | #include "BKE_curve.h" | ||||
| /* -- */ | /* -- */ | ||||
| #include "BKE_object.h" | #include "BKE_object.h" | ||||
| /* -- */ | /* -- */ | ||||
| #include "BKE_pointcloud.h" | #include "BKE_pointcloud.h" | ||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| ▲ Show 20 Lines • Show All 506 Lines • ▼ Show 20 Lines | Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob) | ||||
| if (ob->runtime.curve_cache) { | if (ob->runtime.curve_cache) { | ||||
| disp = ob->runtime.curve_cache->disp; | disp = ob->runtime.curve_cache->disp; | ||||
| } | } | ||||
| return BKE_mesh_new_nomain_from_curve_displist(ob, &disp); | return BKE_mesh_new_nomain_from_curve_displist(ob, &disp); | ||||
| } | } | ||||
| static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char *obdata_name) | |||||
| { | |||||
| if (ob->runtime.data_eval && GS(((ID *)ob->runtime.data_eval)->name) != ID_ME) { | |||||
| return; | |||||
| } | |||||
| Mesh *me_eval = (Mesh *)ob->runtime.data_eval; | |||||
| Mesh *me; | |||||
| MVert *allvert = nullptr; | |||||
| MEdge *alledge = nullptr; | |||||
| MLoop *allloop = nullptr; | |||||
| MLoopUV *alluv = nullptr; | |||||
| MPoly *allpoly = nullptr; | |||||
| int totvert, totedge, totloop, totpoly; | |||||
| Curve *cu = (Curve *)ob->data; | |||||
| if (me_eval == nullptr) { | |||||
| if (mesh_nurbs_displist_to_mdata(cu, | |||||
| dispbase, | |||||
| &allvert, | |||||
| &totvert, | |||||
| &alledge, | |||||
| &totedge, | |||||
| &allloop, | |||||
| &allpoly, | |||||
| &alluv, | |||||
| &totloop, | |||||
| &totpoly) != 0) { | |||||
| /* Error initializing */ | |||||
| return; | |||||
| } | |||||
| /* make mesh */ | |||||
| me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name); | |||||
| me->totvert = totvert; | |||||
| me->totedge = totedge; | |||||
| me->totloop = totloop; | |||||
| me->totpoly = totpoly; | |||||
| me->mvert = (MVert *)CustomData_add_layer( | |||||
| &me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert); | |||||
| me->medge = (MEdge *)CustomData_add_layer( | |||||
| &me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge); | |||||
| me->mloop = (MLoop *)CustomData_add_layer( | |||||
| &me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop); | |||||
| me->mpoly = (MPoly *)CustomData_add_layer( | |||||
| &me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly); | |||||
| if (alluv) { | |||||
| const char *uvname = "UVMap"; | |||||
| me->mloopuv = (MLoopUV *)CustomData_add_layer_named( | |||||
| &me->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, me->totloop, uvname); | |||||
| } | |||||
| BKE_mesh_calc_normals(me); | |||||
| } | |||||
| else { | |||||
| me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name); | |||||
| ob->runtime.data_eval = nullptr; | |||||
| BKE_mesh_nomain_to_mesh(me_eval, me, ob, &CD_MASK_MESH, true); | |||||
| } | |||||
| me->totcol = cu->totcol; | |||||
| me->mat = cu->mat; | |||||
| mesh_copy_texture_space_from_curve_type(cu, me); | |||||
| cu->mat = nullptr; | |||||
| cu->totcol = 0; | |||||
| /* Do not decrement ob->data usercount here, | |||||
| * it's done at end of func with BKE_id_free_us() call. */ | |||||
| ob->data = me; | |||||
| ob->type = OB_MESH; | |||||
| /* For temporary objects in BKE_mesh_new_from_object don't remap | |||||
| * the entire scene with associated depsgraph updates, which are | |||||
| * problematic for renderers exporting data. */ | |||||
| BKE_id_free(nullptr, cu); | |||||
| } | |||||
| struct EdgeLink { | struct EdgeLink { | ||||
| struct EdgeLink *next, *prev; | struct EdgeLink *next, *prev; | ||||
| void *edge; | void *edge; | ||||
| }; | }; | ||||
| struct VertLink { | struct VertLink { | ||||
| Link *next, *prev; | Link *next, *prev; | ||||
| uint index; | uint index; | ||||
| ▲ Show 20 Lines • Show All 275 Lines • ▼ Show 20 Lines | void BKE_pointcloud_to_mesh(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), Object *ob) | ||||
| id_us_min(&((PointCloud *)ob->data)->id); | id_us_min(&((PointCloud *)ob->data)->id); | ||||
| ob->data = me; | ob->data = me; | ||||
| ob->type = OB_MESH; | ob->type = OB_MESH; | ||||
| BKE_object_free_derived_caches(ob); | BKE_object_free_derived_caches(ob); | ||||
| } | } | ||||
| /* Create a temporary object to be used for nurbs-to-mesh conversion. | /* Create a temporary object to be used for nurbs-to-mesh conversion. */ | ||||
| * | static Object *object_for_curve_to_mesh_create(const Object *object) | ||||
| * This is more complex that it should be because #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) | |||||
| { | { | ||||
| Curve *curve = (Curve *)object->data; | const Curve *curve = (const Curve *)object->data; | ||||
| /* Create object itself. */ | /* Create object itself. Set copy on write flags since that is expected for curve evaluation. */ | ||||
| Object *temp_object = (Object *)BKE_id_copy_ex( | Object *temp_object = (Object *)BKE_id_copy_ex( | ||||
| nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE); | nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_SET_COPIED_ON_WRITE); | ||||
JacquesLucke: Where is this checked for exactly? Why wasn't this necessary before? | |||||
Done Inline ActionsI added more detail to the comment. The function I removed from displist.cc did not assign evaluated data. HooglyBoogly: I added more detail to the comment. The function I removed from `displist.cc` did not assign… | |||||
Not Done Inline ActionsThe comment on LIB_ID_COPY_SET_COPIED_ON_WRITE says that LIB_TAG_COPIED_ON_WRITE has to be set as well. I wonder if there is a better way that avoids this flag hackery. Wouldn't be the first time though. And fortunately, this object is very short lived, so it might not be too bad. JacquesLucke: The comment on `LIB_ID_COPY_SET_COPIED_ON_WRITE` says that `LIB_TAG_COPIED_ON_WRITE` has to be… | |||||
Done Inline ActionsI think "must" in the comment for this flag should actually be "will", that seems to describe the behavior. HooglyBoogly: I think "must" in the comment for this flag should actually be "will", that seems to describe… | |||||
Not Done Inline ActionsAn alternative would be to create a temporary depsgraph only for this object and its dependencies. That should be possible without too much hackery. Might be overkill for now, but something to keep in mind in the future. Maybe write that in a comment. I've only done that once a while ago while testing something with particle nodes, but I did some cleanup on the related api back then (rB6a4f5e6a8c399). One probably needs DEG_graph_new, DEG_graph_build_from_ids, DEG_evaluate_on_refresh, DEG_get_evaluated_object and DEG_graph_free. JacquesLucke: An alternative would be to create a temporary depsgraph only for this object and its… | |||||
| /* Remove all modifiers, since we don't want them to be applied. */ | /* Remove all modifiers, since we don't want them to be applied. */ | ||||
| BKE_object_free_modifiers(temp_object, LIB_ID_CREATE_NO_USER_REFCOUNT); | BKE_object_free_modifiers(temp_object, LIB_ID_CREATE_NO_USER_REFCOUNT); | ||||
| /* Copy relevant evaluated fields of curve cache. | /* Need to create copy of curve itself as well, since it will be changed by the curve evaluation | ||||
| * | * process. NOTE: Copies the data, but not the shapekeys. */ | ||||
| * Note that there are extra fields in there like bevel and path, but those are not needed during | temp_object->data = BKE_id_copy_ex(nullptr, | ||||
| * conversion, so they are not copied to save unnecessary allocations. */ | (const ID *)object->data, | ||||
| if (temp_object->runtime.curve_cache == nullptr) { | nullptr, | ||||
| temp_object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), | LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_SET_COPIED_ON_WRITE); | ||||
| "CurveCache for curve types"); | |||||
| } | |||||
| if (object->runtime.curve_cache != nullptr) { | |||||
| BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp); | |||||
| } | |||||
| /* Constructive modifiers will use mesh to store result. */ | |||||
| if (object->runtime.data_eval != nullptr) { | |||||
| BKE_id_copy_ex( | |||||
| nullptr, object->runtime.data_eval, &temp_object->runtime.data_eval, LIB_ID_COPY_LOCALIZE); | |||||
| } | |||||
| /* 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( | |||||
| nullptr, (const ID *)object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE); | |||||
| Curve *temp_curve = (Curve *)temp_object->data; | Curve *temp_curve = (Curve *)temp_object->data; | ||||
| /* Make sure texture space is calculated for a copy of curve, it will be used for the final | /* Make sure texture space is calculated for a copy of curve, it may be used for the final | ||||
| * result. */ | * result. */ | ||||
| BKE_curve_texspace_calc(temp_curve); | BKE_curve_texspace_calc(temp_curve); | ||||
| /* Temporarily set edit so we get updates from edit mode, but also because for text datablocks | /* 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. */ | * copying it while in edit mode gives invalid data structures. */ | ||||
| temp_curve->editfont = curve->editfont; | temp_curve->editfont = curve->editfont; | ||||
| temp_curve->editnurb = curve->editnurb; | temp_curve->editnurb = curve->editnurb; | ||||
| return temp_object; | return temp_object; | ||||
| } | } | ||||
| /** | /** | ||||
| * Populate `object->runtime.curve_cache` which is then used to create the mesh. | * Populate `object->runtime.curve_cache` which is then used to create the mesh. | ||||
| */ | */ | ||||
| static void curve_to_mesh_eval_ensure(Object *object) | static void curve_to_mesh_eval_ensure(Object &object) | ||||
| { | { | ||||
| Curve *curve = (Curve *)object->data; | BLI_assert(GS(static_cast<ID *>(object.data)->name) == ID_CU); | ||||
| Curve remapped_curve = *curve; | Curve &curve = *static_cast<Curve *>(object.data); | ||||
| Object remapped_object = *object; | |||||
| BKE_object_runtime_reset(&remapped_object); | |||||
| remapped_object.data = &remapped_curve; | |||||
| if (object->runtime.curve_cache == nullptr) { | |||||
| object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), | |||||
| "CurveCache for Curve"); | |||||
| } | |||||
| /* Temporarily share the curve-cache with the temporary object, owned by `object`. */ | |||||
| remapped_object.runtime.curve_cache = object->runtime.curve_cache; | |||||
| /* Clear all modifiers for the bevel object. | /* Clear all modifiers for the bevel object. | ||||
| * | * | ||||
| * This is because they can not be reliably evaluated for an original object (at least because | * This is because they can not be reliably evaluated for an original object (at least because | ||||
| * the state of dependencies is not know). | * 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 | * So we create temporary copy of the object which will use same data as the original bevel, but | ||||
| * will have no modifiers. */ | * will have no modifiers. */ | ||||
| Object bevel_object = {{nullptr}}; | Object bevel_object = {{nullptr}}; | ||||
| if (remapped_curve.bevobj != nullptr) { | if (curve.bevobj != nullptr) { | ||||
| bevel_object = *remapped_curve.bevobj; | bevel_object = *curve.bevobj; | ||||
| BLI_listbase_clear(&bevel_object.modifiers); | BLI_listbase_clear(&bevel_object.modifiers); | ||||
| BKE_object_runtime_reset(&bevel_object); | BKE_object_runtime_reset(&bevel_object); | ||||
| remapped_curve.bevobj = &bevel_object; | curve.bevobj = &bevel_object; | ||||
| } | } | ||||
| /* Same thing for taper. */ | /* Same thing for taper. */ | ||||
| Object taper_object = {{nullptr}}; | Object taper_object = {{nullptr}}; | ||||
| if (remapped_curve.taperobj != nullptr) { | if (curve.taperobj != nullptr) { | ||||
| taper_object = *remapped_curve.taperobj; | taper_object = *curve.taperobj; | ||||
| BLI_listbase_clear(&taper_object.modifiers); | BLI_listbase_clear(&taper_object.modifiers); | ||||
| BKE_object_runtime_reset(&taper_object); | BKE_object_runtime_reset(&taper_object); | ||||
| remapped_curve.taperobj = &taper_object; | curve.taperobj = &taper_object; | ||||
| } | } | ||||
| /* NOTE: We don't have dependency graph or scene here, so we pass nullptr. This is all fine since | /* NOTE: We don't have dependency graph or scene here, so we pass nullptr. This is all fine since | ||||
| * they are only used for modifier stack, which we have explicitly disabled for all objects. | * 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 | * TODO(sergey): This is a very fragile logic, but proper solution requires re-writing quite a | ||||
| * bit of internal functions (#mesh_from_nurbs_displist, BKE_mesh_nomain_to_mesh) and also | * bit of internal functions (#BKE_mesh_nomain_to_mesh) and also Mesh From Curve operator. | ||||
| * Mesh From Curve operator. | |||||
| * Brecht says hold off with that. */ | * Brecht says hold off with that. */ | ||||
| Mesh *mesh_eval = nullptr; | BKE_displist_make_curveTypes(nullptr, nullptr, &object, true); | ||||
| BKE_displist_make_curveTypes_forRender( | |||||
| nullptr, nullptr, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval); | |||||
| /* NOTE: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a | BKE_object_runtime_free_data(&taper_object); | ||||
| * real issue currently, code here is broken in more than one way, fix(es) will be done | BKE_object_runtime_free_data(&taper_object); | ||||
Done Inline Actionsdouble free JacquesLucke: double free | |||||
Done Inline ActionsThanks, good catch. HooglyBoogly: Thanks, good catch. | |||||
| * separately. */ | |||||
| if (mesh_eval != nullptr) { | |||||
| BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true); | |||||
| } | } | ||||
| /* Owned by `object` & needed by the caller to create the mesh. */ | /* Necessary because #BKE_object_get_evaluated_mesh doesn't look in the geometry set yet. */ | ||||
| remapped_object.runtime.curve_cache = nullptr; | static const Mesh *get_evaluated_mesh_from_object(const Object *object) | ||||
| { | |||||
| const Mesh *mesh = BKE_object_get_evaluated_mesh(object); | |||||
| if (mesh) { | |||||
| return mesh; | |||||
| } | |||||
| GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval; | |||||
| if (geometry_set_eval) { | |||||
| return geometry_set_eval->get_mesh_for_read(); | |||||
| } | |||||
| return nullptr; | |||||
| } | |||||
| BKE_object_runtime_free_data(&remapped_object); | static const CurveEval *get_evaluated_curve_from_object(const Object *object) | ||||
| BKE_object_runtime_free_data(&taper_object); | { | ||||
| BKE_object_runtime_free_data(&taper_object); | GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval; | ||||
| if (geometry_set_eval) { | |||||
| return geometry_set_eval->get_curve_for_read(); | |||||
| } | |||||
| return nullptr; | |||||
| } | } | ||||
| static Mesh *mesh_new_from_curve_type_object(Object *object) | /* TODO: Use mesh to curve API in geometry module. */ | ||||
| static Mesh *mesh_from_curve_wire_edges(const CurveEval &curve) | |||||
| { | { | ||||
| Curve *curve = (Curve *)object->data; | return BKE_mesh_new_nomain(0, 0, 0, 0, 0); | ||||
| Object *temp_object = object_for_curve_to_mesh_create(object); | } | ||||
| Curve *temp_curve = (Curve *)temp_object->data; | |||||
| /* When input object is an original one, we don't have evaluated curve cache yet, so need to | static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evalauted_object) | ||||
| * create it in the temporary object. */ | { | ||||
| if (!DEG_is_evaluated_object(object)) { | const Mesh *mesh = get_evaluated_mesh_from_object(evalauted_object); | ||||
| curve_to_mesh_eval_ensure(temp_object); | if (mesh) { | ||||
| } | return BKE_mesh_copy_for_eval(mesh, false); | ||||
| } | |||||
| /* Reset pointers before conversion. */ | const CurveEval *curve = get_evaluated_curve_from_object(evalauted_object); | ||||
| temp_curve->editfont = nullptr; | if (curve) { | ||||
| temp_curve->editnurb = nullptr; | return mesh_from_curve_wire_edges(*curve); | ||||
| } | |||||
| /* Convert to mesh. */ | |||||
| mesh_from_nurbs_displist( | |||||
| temp_object, &temp_object->runtime.curve_cache->disp, curve->id.name + 2); | |||||
| /* #mesh_from_nurbs_displist 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(nullptr, temp_object->data); | |||||
| BKE_id_free(nullptr, temp_object); | |||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| Mesh *mesh_result = (Mesh *)temp_object->data; | static Mesh *mesh_new_from_curve_type_object(const Object *object) | ||||
| { | |||||
| /* If the object is evaluated, it should either have an evaluated mesh or curve data already. | |||||
| * The mesh can be duplicated, or the curve converted to wire mesh edges. */ | |||||
| if (DEG_is_evaluated_object(object)) { | |||||
| return mesh_new_from_evaluated_curve_type_object(object); | |||||
| } | |||||
Not Done Inline Actionstypo (evalauted_object) JacquesLucke: typo (`evalauted_object`) | |||||
| BKE_id_free(nullptr, temp_object); | /* Otherwise, create a temporary "fake" evaluated object and try again. This might have | ||||
| * different results, since in order to avoid having adverse affects to other original objects, | |||||
| * modifiers are cleared. */ | |||||
| Object *temp_object = object_for_curve_to_mesh_create(object); | |||||
| ID *temp_data = static_cast<ID *>(temp_object->data); | |||||
| curve_to_mesh_eval_ensure(*temp_object); | |||||
| /* NOTE: Materials are copied in #mesh_from_nurbs_displist(). */ | /* If evaluating the curve replaced object data with different data, free the original data. */ | ||||
| if (temp_data != temp_object->data) { | |||||
| BKE_id_free(nullptr, temp_data); | |||||
| } | |||||
| return mesh_result; | Mesh *mesh = mesh_new_from_evaluated_curve_type_object(temp_object); | ||||
| BKE_id_free(nullptr, temp_object->data); | |||||
| BKE_id_free(nullptr, temp_object); | |||||
| return mesh; | |||||
| } | } | ||||
| static Mesh *mesh_new_from_mball_object(Object *object) | static Mesh *mesh_new_from_mball_object(Object *object) | ||||
| { | { | ||||
| MetaBall *mball = (MetaBall *)object->data; | MetaBall *mball = (MetaBall *)object->data; | ||||
| /* NOTE: We can only create mesh for a polygonized meta ball. This figures out all original meta | /* 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 | * balls and all evaluated child meta balls (since polygonization is only stored in the mother | ||||
| ▲ Show 20 Lines • Show All 579 Lines • Show Last 20 Lines | |||||
Where is this checked for exactly? Why wasn't this necessary before?