Fix D12533: Simplify curve object to mesh conversion

This patch simplifies the curve object to mesh conversion
used by the object convert operator and exporters.

The existing code had a convoluted model of ownership, and did quite
a bit of unnecessary work. It also assumed that curve objects always
evaluated to a mesh, which is not the case anymore.

Now the code checks if the object it receives is evaluated. If so,
it can simply return a copy of the evaluated mesh (or convert the
evaluated curve wire edges to a mesh if there was no evaluated mesh).
If the object isn't evaluated, it uses a temporary copy of the object
with modifiers removed to create the mesh in the same way.

This follows up on the recent changes to curve evaluation,
namely that the result is always either a mesh or a wire curve.

Differential Revision: https://developer.blender.org/D12533
This commit is contained in:
Hans Goudey 2021-09-23 11:41:46 -05:00
parent ed541de29d
commit eb0eb54d96
Notes: blender-bot 2023-02-14 08:10:06 +01:00
Referenced by commit fb820496f5, Tracking: Sort motion tracking tracks by start and end frames
Referenced by issue #94454, Crash in nvoglv64.dll entering edit mode on curve
Referenced by issue #91445, Convert curve object data to mesh does not work when curve does not evaluate to mesh
3 changed files with 83 additions and 206 deletions

View File

@ -87,11 +87,6 @@ void BKE_displist_make_curveTypes(struct Depsgraph *depsgraph,
const struct Scene *scene,
struct Object *ob,
const bool for_render);
void BKE_displist_make_curveTypes_forRender(struct Depsgraph *depsgraph,
const struct Scene *scene,
struct Object *ob,
struct ListBase *dispbase,
struct Mesh **r_final);
void BKE_displist_make_mball(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob);
void BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph,

View File

@ -1540,23 +1540,6 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
boundbox_displist_object(ob);
}
void BKE_displist_make_curveTypes_forRender(
Depsgraph *depsgraph, const Scene *scene, Object *ob, ListBase *r_dispbase, Mesh **r_final)
{
if (ob->runtime.curve_cache == nullptr) {
ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), __func__);
}
if (ob->type == OB_SURF) {
evaluate_surface_object(depsgraph, scene, ob, true, r_dispbase, r_final);
}
else {
GeometrySet geometry_set = evaluate_curve_type_object(depsgraph, scene, ob, true, r_dispbase);
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
*r_final = mesh_component.release();
}
}
void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3])
{
bool doit = false;

View File

@ -41,6 +41,7 @@
#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
#include "BKE_geometry_set.hh"
#include "BKE_key.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
@ -51,6 +52,7 @@
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
#include "BKE_modifier.h"
#include "BKE_spline.hh"
/* these 2 are only used by conversion functions */
#include "BKE_curve.h"
/* -- */
@ -58,6 +60,8 @@
/* -- */
#include "BKE_pointcloud.h"
#include "BKE_curve_to_mesh.hh"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@ -573,90 +577,6 @@ Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob)
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 *next, *prev;
void *edge;
@ -948,47 +868,25 @@ void BKE_pointcloud_to_mesh(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(sce
BKE_object_free_derived_caches(ob);
}
/* Create a temporary object to be used for nurbs-to-mesh conversion.
*
* 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)
/* Create a temporary object to be used for nurbs-to-mesh conversion. */
static Object *object_for_curve_to_mesh_create(const Object *object)
{
Curve *curve = (Curve *)object->data;
const Curve *curve = (const Curve *)object->data;
/* Create object itself. */
/* reate a temporary object which can be evaluated and modified by generic
* curve evaluation (hence the LIB_ID_COPY_SET_COPIED_ON_WRITE flag). */
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);
/* 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 (temp_object->runtime.curve_cache == nullptr) {
temp_object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
"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);
/* 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. */
temp_object->data = BKE_id_copy_ex(nullptr,
(const ID *)object->data,
nullptr,
LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_SET_COPIED_ON_WRITE);
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
@ -1006,23 +904,10 @@ static Object *object_for_curve_to_mesh_create(Object *object)
/**
* 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;
Curve remapped_curve = *curve;
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;
BLI_assert(GS(static_cast<ID *>(object.data)->name) == ID_CU);
Curve &curve = *static_cast<Curve *>(object.data);
/* Clear all modifiers for the bevel object.
*
* This is because they can not be reliably evaluated for an original object (at least because
@ -1031,83 +916,97 @@ static void curve_to_mesh_eval_ensure(Object *object)
* 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 = {{nullptr}};
if (remapped_curve.bevobj != nullptr) {
bevel_object = *remapped_curve.bevobj;
if (curve.bevobj != nullptr) {
bevel_object = *curve.bevobj;
BLI_listbase_clear(&bevel_object.modifiers);
BKE_object_runtime_reset(&bevel_object);
remapped_curve.bevobj = &bevel_object;
curve.bevobj = &bevel_object;
}
/* Same thing for taper. */
Object taper_object = {{nullptr}};
if (remapped_curve.taperobj != nullptr) {
taper_object = *remapped_curve.taperobj;
if (curve.taperobj != nullptr) {
taper_object = *curve.taperobj;
BLI_listbase_clear(&taper_object.modifiers);
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
* 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 (#mesh_from_nurbs_displist, BKE_mesh_nomain_to_mesh) and also
* Mesh From Curve operator.
* bit of internal functions (#BKE_mesh_nomain_to_mesh) and also Mesh From Curve operator.
* Brecht says hold off with that. */
Mesh *mesh_eval = nullptr;
BKE_displist_make_curveTypes_forRender(
nullptr, nullptr, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval);
BKE_displist_make_curveTypes(nullptr, nullptr, &object, true);
/* NOTE: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a
* real issue currently, code here is broken in more than one way, fix(es) will be done
* 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. */
remapped_object.runtime.curve_cache = nullptr;
BKE_object_runtime_free_data(&remapped_object);
BKE_object_runtime_free_data(&taper_object);
BKE_object_runtime_free_data(&bevel_object);
BKE_object_runtime_free_data(&taper_object);
}
static Mesh *mesh_new_from_curve_type_object(Object *object)
/* Necessary because #BKE_object_get_evaluated_mesh doesn't look in the geometry set yet. */
static const Mesh *get_evaluated_mesh_from_object(const Object *object)
{
Curve *curve = (Curve *)object->data;
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;
}
static const CurveEval *get_evaluated_curve_from_object(const Object *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_evaluated_curve_type_object(const Object *evaluated_object)
{
const Mesh *mesh = get_evaluated_mesh_from_object(evaluated_object);
if (mesh) {
return BKE_mesh_copy_for_eval(mesh, false);
}
const CurveEval *curve = get_evaluated_curve_from_object(evaluated_object);
if (curve) {
return blender::bke::curve_to_wire_mesh(*curve);
}
return nullptr;
}
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);
}
/* 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. An alternative would be to create a temporary depsgraph only for this
* object and its dependencies. */
Object *temp_object = object_for_curve_to_mesh_create(object);
Curve *temp_curve = (Curve *)temp_object->data;
ID *temp_data = static_cast<ID *>(temp_object->data);
curve_to_mesh_eval_ensure(*temp_object);
/* 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);
/* 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);
}
/* Reset pointers before conversion. */
temp_curve->editfont = nullptr;
temp_curve->editnurb = nullptr;
/* 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;
}
Mesh *mesh_result = (Mesh *)temp_object->data;
Mesh *mesh = mesh_new_from_evaluated_curve_type_object(temp_object);
BKE_id_free(nullptr, temp_object->data);
BKE_id_free(nullptr, temp_object);
/* NOTE: Materials are copied in #mesh_from_nurbs_displist(). */
return mesh_result;
return mesh;
}
static Mesh *mesh_new_from_mball_object(Object *object)