Fix #143450: Bevel modifier crash with free normals
If the BMesh already has a "custom_normal" attribute with the wrong type, the call to `BM_lnorspace_update` won't be able to add the attribute with the expected name, and the bevel code ends up using an invalid offset to access the data. For the fix, first just guard against that case. But also make sure the harden normals functionality still works when the input mesh has free normals. They will no be converted to tangent space normals as necessary, in bevel and in other BMesh code that requires that custom normal storage format. Pull Request: https://projects.blender.org/blender/blender/pulls/143489
This commit is contained in:
@@ -1877,14 +1877,73 @@ void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the corner fan (tangent space) style custom normals exist on the BMesh. If free vector
|
||||
* custom normals exist, they'll be converted. This is often necessary for BMesh editing tools that
|
||||
* don't (yet) support free normals.
|
||||
*/
|
||||
static void bm_lnorspace_ensure_from_free_normals(BMesh *bm)
|
||||
{
|
||||
/* Zero values tell the normals calculation code to use the automatic normals (rather than any
|
||||
* custom normal vector). */
|
||||
Array<float3> lnors(bm->totloop, float3(0));
|
||||
const int vert_free_offset = CustomData_get_offset_named(
|
||||
&bm->vdata, CD_PROP_FLOAT3, "custom_normal");
|
||||
const int edge_free_offset = CustomData_get_offset_named(
|
||||
&bm->edata, CD_PROP_FLOAT3, "custom_normal");
|
||||
const int face_free_offset = CustomData_get_offset_named(
|
||||
&bm->pdata, CD_PROP_FLOAT3, "custom_normal");
|
||||
const int loop_free_offset = CustomData_get_offset_named(
|
||||
&bm->ldata, CD_PROP_FLOAT3, "custom_normal");
|
||||
if (vert_free_offset != -1) {
|
||||
int loop_index = 0;
|
||||
BMFace *f;
|
||||
BMLoop *l;
|
||||
BMIter fiter, liter;
|
||||
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
||||
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
|
||||
lnors[loop_index++] = float3(BM_ELEM_CD_GET_FLOAT_P(l->v, vert_free_offset));
|
||||
}
|
||||
}
|
||||
BM_data_layer_free_named(bm, &bm->vdata, "custom_normal");
|
||||
}
|
||||
else if (edge_free_offset != -1) {
|
||||
BM_data_layer_free_named(bm, &bm->edata, "custom_normal");
|
||||
}
|
||||
else if (face_free_offset != -1) {
|
||||
int loop_index = 0;
|
||||
BMFace *f;
|
||||
BMLoop *l;
|
||||
BMIter fiter, liter;
|
||||
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
||||
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
|
||||
lnors[loop_index++] = float3(BM_ELEM_CD_GET_FLOAT_P(f, face_free_offset));
|
||||
}
|
||||
}
|
||||
BM_data_layer_free_named(bm, &bm->pdata, "custom_normal");
|
||||
}
|
||||
else if (loop_free_offset != -1) {
|
||||
int loop_index = 0;
|
||||
BMFace *f;
|
||||
BMLoop *l;
|
||||
BMIter fiter, liter;
|
||||
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
||||
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
|
||||
lnors[loop_index++] = float3(BM_ELEM_CD_GET_FLOAT_P(l, loop_free_offset));
|
||||
}
|
||||
}
|
||||
BM_data_layer_free_named(bm, &bm->ldata, "custom_normal");
|
||||
}
|
||||
BM_lnorspacearr_store(bm, lnors);
|
||||
}
|
||||
|
||||
void BM_lnorspace_update(BMesh *bm)
|
||||
{
|
||||
if (bm->lnor_spacearr == nullptr) {
|
||||
bm->lnor_spacearr = MEM_callocN<MLoopNorSpaceArray>(__func__);
|
||||
}
|
||||
if (bm->lnor_spacearr->lspacearr == nullptr) {
|
||||
Array<float3> lnors(bm->totloop, float3(0));
|
||||
BM_lnorspacearr_store(bm, lnors);
|
||||
bm_lnorspace_ensure_from_free_normals(bm);
|
||||
}
|
||||
else if (bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL)) {
|
||||
BM_lnorspace_rebuild(bm, false);
|
||||
|
||||
@@ -2675,6 +2675,12 @@ static void bevel_harden_normals(BevelParams *bp, BMesh *bm)
|
||||
cd_clnors_offset = CustomData_get_offset_named(&bm->ldata, CD_PROP_INT16_2D, "custom_normal");
|
||||
}
|
||||
|
||||
/* If the custom normals attribute still hasn't been added with the correct type, at least don't
|
||||
* crash. */
|
||||
if (cd_clnors_offset == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
BMIter fiter;
|
||||
BMFace *f;
|
||||
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
||||
|
||||
Reference in New Issue
Block a user