Fix T41085: Blender crashes when using "spline" or "segments" for bevel curve factor.

Root of the issue goes to the fact that bevel list calculation might drop some points
if they're at the same position. This made spline length calculation goes wrong.

Now the length of the bevel segments is stored in the bevel list, so values are
always reliable.

Initial patch by Lukas Treyer with some tweaks from me.
This commit is contained in:
Sergey Sharybin 2014-08-18 17:35:51 +06:00
parent 05cb63ecf7
commit 6c7467e0da
Notes: blender-bot 2023-02-14 10:20:06 +01:00
Referenced by issue #41085, Blender crashes when using "spline" or "segments" for bevel curve factor.
5 changed files with 210 additions and 133 deletions

View File

@ -111,6 +111,7 @@ struct ListBase *BKE_curve_editNurbs_get(struct Curve *cu);
float *BKE_curve_make_orco(struct Scene *scene, struct Object *ob, int *r_numVerts);
float *BKE_curve_surf_make_orco(struct Object *ob);
void BKE_curve_bevelList_free(struct ListBase *bev);
void BKE_curve_bevelList_make(struct Object *ob, struct ListBase *nurbs, bool for_render);
void BKE_curve_bevel_make(struct Scene *scene, struct Object *ob, struct ListBase *disp,
const bool for_render, const bool use_render_resolution);

View File

@ -2500,6 +2500,21 @@ static void bevlist_firstlast_direction_calc_from_bpoint(Nurb *nu, BevList *bl)
}
}
void BKE_curve_bevelList_free(ListBase *bev)
{
BevList *bl, *blnext;
for (bl = bev->first; bl != NULL; bl = blnext) {
blnext = bl->next;
if (bl->seglen != NULL) {
MEM_freeN(bl->seglen);
}
if (bl->segbevcount != NULL) {
MEM_freeN(bl->segbevcount);
}
MEM_freeN(bl);
}
}
void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
{
/*
@ -2508,21 +2523,29 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
* - separate in individual blocks with BoundBox
* - AutoHole detection
*/
Curve *cu;
/* this function needs an object, because of tflag and upflag */
Curve *cu = ob->data;
Nurb *nu;
BezTriple *bezt, *prevbezt;
BPoint *bp;
BevList *bl, *blnew, *blnext;
BevPoint *bevp, *bevp2, *bevp1 = NULL, *bevp0;
const float treshold = 0.00001f;
float min, inp;
float *seglen;
struct BevelSort *sortdata, *sd, *sd1;
int a, b, nr, poly, resolu = 0, len = 0;
int a, b, nr, poly, resolu = 0, len = 0, segcount;
int *segbevcount;
bool do_tilt, do_radius, do_weight;
bool is_editmode = false;
ListBase *bev;
/* this function needs an object, because of tflag and upflag */
cu = ob->data;
/* segbevcount alsp requires seglen. */
const bool need_seglen =
ELEM(cu->bevfac1_mapping,CU_BEVFAC_MAP_SEGMENT, CU_BEVFAC_MAP_SPLINE) ||
ELEM(cu->bevfac2_mapping,CU_BEVFAC_MAP_SEGMENT, CU_BEVFAC_MAP_SPLINE);
bev = &ob->curve_cache->bev;
@ -2531,7 +2554,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
/* STEP 1: MAKE POLYS */
BLI_freelistN(&(ob->curve_cache->bev));
BKE_curve_bevelList_free(&ob->curve_cache->bev);
nu = nurbs->first;
if (cu->editnurb && ob->type != OB_FONT) {
is_editmode = 1;
@ -2561,9 +2584,15 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
else
resolu = nu->resolu;
segcount = SEGMENTSU(nu);
if (nu->type == CU_POLY) {
len = nu->pntsu;
bl = MEM_callocN(sizeof(BevList) + len * sizeof(BevPoint), "makeBevelList2");
if (need_seglen) {
bl->seglen = MEM_mallocN(segcount * sizeof(float), "makeBevelList2_seglen");
bl->segbevcount = MEM_mallocN(segcount * sizeof(int), "makeBevelList2_segbevcount");
}
BLI_addtail(bev, bl);
bl->poly = (nu->flagu & CU_NURB_CYCLIC) ? 0 : -1;
@ -2571,7 +2600,11 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bl->dupe_nr = 0;
bl->charidx = nu->charidx;
bevp = bl->bevpoints;
bevp->offset = 0;
bp = nu->bp;
seglen = bl->seglen;
segbevcount = bl->segbevcount;
BLI_assert(segcount >= len);
while (len--) {
copy_v3_v3(bevp->vec, bp->vec);
@ -2579,23 +2612,53 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bevp->radius = bp->radius;
bevp->weight = bp->weight;
bevp->split_tag = true;
bevp++;
bp++;
if (seglen != NULL) {
*seglen = len_v3v3(bevp->vec, bp->vec);
bevp++;
bevp->offset = *seglen;
if (*seglen > treshold) *segbevcount = 1;
else *segbevcount = 0;
seglen++;
segbevcount++;
}
else {
bevp++;
}
}
if ((nu->flagu & CU_NURB_CYCLIC) == 0) {
bevlist_firstlast_direction_calc_from_bpoint(nu, bl);
if (seglen != NULL) {
*seglen = len_v3v3(bevp->vec, nu->bp->vec);
bl->bevpoints->offset = *seglen;
*segbevcount = 1;
}
}
}
else if (nu->type == CU_BEZIER) {
/* in case last point is not cyclic */
len = resolu * (nu->pntsu + (nu->flagu & CU_NURB_CYCLIC) - 1) + 1;
len = segcount * resolu + 1;
bl = MEM_callocN(sizeof(BevList) + len * sizeof(BevPoint), "makeBevelBPoints");
if (need_seglen) {
bl->seglen = MEM_mallocN(segcount * sizeof(float), "makeBevelBPoints_seglen");
bl->segbevcount = MEM_mallocN(segcount * sizeof(int), "makeBevelBPoints_segbevcount");
}
BLI_addtail(bev, bl);
bl->poly = (nu->flagu & CU_NURB_CYCLIC) ? 0 : -1;
bl->charidx = nu->charidx;
bevp = bl->bevpoints;
seglen = bl->seglen;
segbevcount = bl->segbevcount;
bevp->offset = 0;
if (seglen != NULL) {
*seglen = 0;
*segbevcount = 0;
}
a = nu->pntsu - 1;
bezt = nu->bezt;
@ -2611,6 +2674,8 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
sub_v3_v3v3(bevp->dir, prevbezt->vec[2], prevbezt->vec[1]);
normalize_v3(bevp->dir);
BLI_assert(segcount >= a);
while (a--) {
if (prevbezt->h2 == HD_VECT && bezt->h1 == HD_VECT) {
@ -2623,6 +2688,14 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
bevp++;
bl->nr++;
bl->dupe_nr = 1;
if (seglen != NULL) {
*seglen = len_v3v3(prevbezt->vec[1], bezt->vec[1]);
bevp->offset = *seglen;
seglen++;
/* match segbevcount to the cleaned up bevel lists (see STEP 2) */
if (bevp->offset > treshold) *segbevcount = 1;
segbevcount++;
}
}
else {
/* always do all three, to prevent data hanging around */
@ -2660,8 +2733,28 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
else if (prevbezt->h2 == 0 || prevbezt->h2 == HD_VECT)
bevp->split_tag = true;
}
/* seglen */
if (seglen != NULL) {
*seglen = 0;
*segbevcount = 0;
for (j = 0; j < resolu; j++) {
bevp0 = bevp;
bevp++;
bevp->offset = len_v3v3(bevp0->vec, bevp->vec);
/* match seglen and segbevcount to the cleaned up bevel lists (see STEP 2) */
if (bevp->offset > treshold) {
*seglen += bevp->offset;
*segbevcount += 1;
}
}
seglen++;
segbevcount++;
}
else {
bevp += resolu;
}
bl->nr += resolu;
bevp += resolu;
}
prevbezt = bezt;
bezt++;
@ -2681,15 +2774,22 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
}
else if (nu->type == CU_NURBS) {
if (nu->pntsv == 1) {
len = (resolu * SEGMENTSU(nu));
len = (resolu * segcount);
bl = MEM_callocN(sizeof(BevList) + len * sizeof(BevPoint), "makeBevelList3");
if (need_seglen) {
bl->seglen = MEM_mallocN(segcount * sizeof(float), "makeBevelList3_seglen");
bl->segbevcount = MEM_mallocN(segcount * sizeof(int), "makeBevelList3_segbevcount");
}
BLI_addtail(bev, bl);
bl->nr = len;
bl->dupe_nr = 0;
bl->poly = (nu->flagu & CU_NURB_CYCLIC) ? 0 : -1;
bl->charidx = nu->charidx;
bevp = bl->bevpoints;
seglen = bl->seglen;
segbevcount = bl->segbevcount;
BKE_nurb_makeCurve(nu, &bevp->vec[0],
do_tilt ? &bevp->alfa : NULL,
@ -2697,6 +2797,31 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
do_weight ? &bevp->weight : NULL,
resolu, sizeof(BevPoint));
/* match seglen and segbevcount to the cleaned up bevel lists (see STEP 2) */
if (seglen != NULL) {
nr = segcount;
bevp0 = bevp;
bevp++;
while (nr) {
int j;
*seglen = 0;
*segbevcount = 0;
/* We keep last bevel segment zero-length. */
for (j = 0; j < ((nr == 1) ? (resolu - 1) : resolu); j++) {
bevp->offset = len_v3v3(bevp0->vec, bevp->vec);
if (bevp->offset > treshold) {
*seglen += bevp->offset;
*segbevcount += 1;
}
bevp0 = bevp;
bevp++;
}
seglen++;
segbevcount++;
nr--;
}
}
if ((nu->flagu & CU_NURB_CYCLIC) == 0) {
bevlist_firstlast_direction_calc_from_bpoint(nu, bl);
}
@ -2717,15 +2842,24 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
}
else {
bevp0 = bl->bevpoints;
bevp0->offset = 0;
bevp1 = bevp0 + 1;
}
nr--;
while (nr--) {
if (fabsf(bevp0->vec[0] - bevp1->vec[0]) < 0.00001f) {
if (fabsf(bevp0->vec[1] - bevp1->vec[1]) < 0.00001f) {
if (fabsf(bevp0->vec[2] - bevp1->vec[2]) < 0.00001f) {
bevp0->dupe_tag = true;
bl->dupe_nr++;
if (seglen != NULL) {
if (fabsf(bevp1->offset) < treshold) {
bevp0->dupe_tag = true;
bl->dupe_nr++;
}
}
else {
if (fabsf(bevp0->vec[0] - bevp1->vec[0]) < 0.00001f) {
if (fabsf(bevp0->vec[1] - bevp1->vec[1]) < 0.00001f) {
if (fabsf(bevp0->vec[2] - bevp1->vec[2]) < 0.00001f) {
bevp0->dupe_tag = true;
bl->dupe_nr++;
}
}
}
}
@ -2742,6 +2876,8 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
nr = bl->nr - bl->dupe_nr + 1; /* +1 because vectorbezier sets flag too */
blnew = MEM_mallocN(sizeof(BevList) + nr * sizeof(BevPoint), "makeBevelList4");
memcpy(blnew, bl, sizeof(BevList));
blnew->segbevcount = bl->segbevcount;
blnew->seglen = bl->seglen;
blnew->nr = 0;
BLI_remlink(bev, bl);
BLI_insertlinkbefore(bev, blnext, blnew); /* to make sure bevlijst is tuned with nurblist */

View File

@ -1356,31 +1356,57 @@ static void fillBevelCap(Nurb *nu, DispList *dlb, float *prev_fp, ListBase *disp
BLI_addtail(dispbase, dl);
}
static void calc_bevfac_segment_mapping(BevList *bl, float bevfac, float spline_length, int *r_bev, float *r_blend)
{
float normlen, normsum = 0.0f;
float *seglen = bl->seglen;
int *segbevcount = bl->segbevcount;
int bevcount = 0, nr = bl->nr;
static void calc_bevfac_spline_mapping(BevList *bl, float bevfac, float spline_length, const float *bevp_array,
float bev_fl = bevfac * (bl->nr - 1);
*r_bev = (int)bev_fl;
while (bevcount < nr - 1) {
normlen = *seglen / spline_length;
if (normsum + normlen > bevfac){
bev_fl = bevcount + (bevfac - normsum) / normlen * *segbevcount;
*r_bev = (int) bev_fl;
*r_blend = bev_fl - *r_bev;
break;
}
normsum += normlen;
bevcount += *segbevcount;
segbevcount++;
seglen++;
}
}
static void calc_bevfac_spline_mapping(BevList *bl, float bevfac,
float spline_length,
int *r_bev, float *r_blend)
{
const float len_target = bevfac * spline_length;
float len = 0.0f;
float len_step = 0.0f;
int i;
for (i = 0; i < bl->nr - 1; i++) {
float len_next;
len_step = bevp_array[i];
len_next = len + len_step;
BevPoint *bevp = bl->bevpoints;
float len_next = 0.0f, len = 0.0f;
int i = 0, nr = bl->nr;
while (nr--) {
bevp++;
len_next = len + bevp->offset;
if (len_next > len_target) {
break;
}
len = len_next;
i++;
}
*r_bev = i;
*r_blend = (len_target - len) / len_step;
*r_blend = (len_target - len) / bevp->offset;
}
static void calc_bevfac_mapping_default(
BevList *bl,
int *r_start, float *r_firstblend, int *r_steps, float *r_lastblend)
static void calc_bevfac_mapping_default(BevList *bl,
int *r_start, float *r_firstblend,
int *r_steps, float *r_lastblend)
{
*r_start = 0;
*r_steps = bl->nr;
@ -1388,19 +1414,11 @@ static void calc_bevfac_mapping_default(
*r_lastblend = 1.0f;
}
static void calc_bevfac_mapping(
Curve *cu, BevList *bl, Nurb *nu, const bool use_render_resolution,
static void calc_bevfac_mapping(Curve *cu, BevList *bl, Nurb *nu,
int *r_start, float *r_firstblend, int *r_steps, float *r_lastblend)
{
const int resolu = (nu->type == CU_POLY) ?
1 : (use_render_resolution && (cu->resolu_ren != 0)) ?
cu->resolu_ren : cu->resolu;
const int segcount = ((nu->type == CU_POLY) ? bl->nr : nu->pntsu) - 1;
float l, startf, endf, tmpf, total_length = 0.0f;
float *bevp_array = NULL;
float *segments = NULL;
int end = 0, i, j;
float tmpf, total_length = 0.0f;
int end = 0, i;
if ((BKE_nurb_check_valid_u(nu) == false) ||
/* not essential, but skips unnecessary calculation */
@ -1411,57 +1429,12 @@ static void calc_bevfac_mapping(
return;
}
if ((cu->bevfac1_mapping != CU_BEVFAC_MAP_RESOLU) ||
(cu->bevfac2_mapping != CU_BEVFAC_MAP_RESOLU))
if (ELEM(cu->bevfac1_mapping,
CU_BEVFAC_MAP_SEGMENT,
CU_BEVFAC_MAP_SPLINE))
{
BezTriple *bezt, *bezt_prev;
BevPoint *bevp, *bevp_prev;
int bevp_i;
bevp_array = MEM_mallocN(sizeof(*bevp_array) * (bl->nr - 1), "bevp_dists");
segments = MEM_callocN(sizeof(*segments) * segcount, "bevp_segmentlengths");
bevp_prev = bl->bevpoints;
bevp = bevp_prev + 1;
if (nu->type == CU_BEZIER) {
bezt_prev = nu->bezt;
bezt = bezt_prev + 1;
for (i = 0, bevp_i = 0; i < segcount; i++, bezt_prev++, bezt++) {
float seglen = 0.0f;
if (bezt_prev->h2 == HD_VECT && bezt->h1 == HD_VECT) {
seglen = len_v3v3(bevp->vec, bevp_prev->vec);
BLI_assert(bevp_i < bl->nr - 1);
bevp_array[bevp_i++] = seglen;
bevp_prev = bevp++;
}
else {
for (j = 0; j < resolu; j++, bevp_prev = bevp++) {
l = len_v3v3(bevp->vec, bevp_prev->vec);
seglen += l;
BLI_assert(bevp_i < bl->nr - 1);
bevp_array[bevp_i++] = l;
}
}
BLI_assert(i < segcount);
segments[i] = seglen;
total_length += seglen;
seglen = 0.0f;
}
}
else {
float seglen = 0.0f;
for (i = 1, j = 0; i < bl->nr; i++, bevp_prev = bevp++) {
BLI_assert(i - 1 < bl->nr);
bevp_array[i - 1] = len_v3v3(bevp->vec, bevp_prev->vec);
total_length += bevp_array[i - 1];
seglen += bevp_array[i - 1];
if ((i % resolu) == 0 || (bl->nr - 1) == i) {
BLI_assert(j < segcount);
segments[j++] = seglen;
seglen = 0.0f;
}
}
for (i = 0; i < SEGMENTSU(nu); i++) {
total_length += bl->seglen[i];
}
}
@ -1470,31 +1443,18 @@ static void calc_bevfac_mapping(
{
const float start_fl = cu->bevfac1 * (bl->nr - 1);
*r_start = (int)start_fl;
*r_firstblend = 1.0f - (start_fl - (*r_start));
break;
}
case CU_BEVFAC_MAP_SEGMENT:
{
float sum = 0.0f;
const float start_fl = cu->bevfac1 * (bl->nr - 1);
*r_start = (int)start_fl;
for (i = 0; i < segcount; i++) {
l = segments[i] / total_length;
if (sum + l > cu->bevfac1) {
startf = i * resolu + (cu->bevfac1 - sum) / l * resolu;
*r_start = (int) startf;
*r_firstblend = 1.0f - (startf - *r_start);
break;
}
sum += l;
}
calc_bevfac_segment_mapping(bl, cu->bevfac1, total_length, r_start, r_firstblend);
*r_firstblend = 1.0f - *r_firstblend;
break;
}
case CU_BEVFAC_MAP_SPLINE:
{
calc_bevfac_spline_mapping(bl, cu->bevfac1, total_length, bevp_array, r_start, r_firstblend);
calc_bevfac_spline_mapping(bl, cu->bevfac1, total_length, r_start, r_firstblend);
*r_firstblend = 1.0f - *r_firstblend;
break;
}
@ -1512,27 +1472,13 @@ static void calc_bevfac_mapping(
}
case CU_BEVFAC_MAP_SEGMENT:
{
float sum = 0.0f;
const float end_fl = cu->bevfac2 * (bl->nr - 1);
end = (int)end_fl;
calc_bevfac_segment_mapping(bl, cu->bevfac2, total_length, &end, r_lastblend);
*r_steps = end - *r_start + 2;
for (i = 0; i < segcount; i++) {
l = segments[i] / total_length;
if (sum + l > cu->bevfac2) {
endf = i * resolu + (cu->bevfac2 - sum) / l * resolu;
end = (int)endf;
*r_lastblend = (endf - end);
*r_steps = end - *r_start + 2;
break;
}
sum += l;
}
break;
}
case CU_BEVFAC_MAP_SPLINE:
{
calc_bevfac_spline_mapping(bl, cu->bevfac2, total_length, bevp_array, &end, r_lastblend);
calc_bevfac_spline_mapping(bl, cu->bevfac2, total_length, &end, r_lastblend);
*r_steps = end - *r_start + 2;
break;
}
@ -1550,13 +1496,6 @@ static void calc_bevfac_mapping(
*r_steps = bl->nr - *r_start;
*r_lastblend = 1.0f;
}
if (bevp_array) {
MEM_freeN(bevp_array);
}
if (segments) {
MEM_freeN(segments);
}
}
static void do_makeDispListCurveTypes(Scene *scene, Object *ob, ListBase *dispbase,
@ -1575,7 +1514,7 @@ static void do_makeDispListCurveTypes(Scene *scene, Object *ob, ListBase *dispba
ListBase dlbev;
ListBase nubase = {NULL, NULL};
BLI_freelistN(&(ob->curve_cache->bev));
BKE_curve_bevelList_free(&ob->curve_cache->bev);
/* We only re-evlauate path if evaluation is not happening for orco.
* If the calculation happens for orco, we should never free data which
@ -1667,8 +1606,7 @@ static void do_makeDispListCurveTypes(Scene *scene, Object *ob, ListBase *dispba
continue;
}
calc_bevfac_mapping(cu, bl, nu, use_render_resolution,
&start, &firstblend, &steps, &lastblend);
calc_bevfac_mapping(cu, bl, nu, &start, &firstblend, &steps, &lastblend);
}
for (dlb = dlbev.first; dlb; dlb = dlb->next) {

View File

@ -180,7 +180,7 @@ void BKE_object_free_curve_cache(Object *ob)
{
if (ob->curve_cache) {
BKE_displist_free(&ob->curve_cache->disp);
BLI_freelistN(&ob->curve_cache->bev);
BKE_curve_bevelList_free(&ob->curve_cache->bev);
if (ob->curve_cache->path) {
free_path(ob->curve_cache->path);
}
@ -322,7 +322,7 @@ void BKE_object_free_derived_caches(Object *ob)
if (ob->curve_cache) {
BKE_displist_free(&ob->curve_cache->disp);
BLI_freelistN(&ob->curve_cache->bev);
BKE_curve_bevelList_free(&ob->curve_cache->bev);
if (ob->curve_cache->path) {
free_path(ob->curve_cache->path);
ob->curve_cache->path = NULL;
@ -408,7 +408,7 @@ void BKE_object_free_ex(Object *ob, bool do_id_user)
/* Free runtime curves data. */
if (ob->curve_cache) {
BLI_freelistN(&ob->curve_cache->bev);
BKE_curve_bevelList_free(&ob->curve_cache->bev);
if (ob->curve_cache->path)
free_path(ob->curve_cache->path);
MEM_freeN(ob->curve_cache);

View File

@ -72,7 +72,7 @@ typedef struct Path {
#
#
typedef struct BevPoint {
float vec[3], alfa, radius, weight;
float vec[3], alfa, radius, weight, offset;
float sina, cosa; /* 2D Only */
float dir[3], tan[3], quat[4]; /* 3D Only */
short split_tag, dupe_tag;
@ -86,6 +86,8 @@ typedef struct BevList {
int nr, dupe_nr;
int poly, hole;
int charidx;
int *segbevcount;
float *seglen;
/* over-alloc */
BevPoint bevpoints[0];