diff options
author | Hans Goudey <h.goudey@me.com> | 2020-09-16 18:20:38 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2020-09-16 18:20:38 +0300 |
commit | 60fa80de0b2c7138fc86b8b688f22a9d2623e8ed (patch) | |
tree | 44d7acc6bf5022866f5369a0151c513d4dfd957e /source | |
parent | 365271a5886e1678b41e636581616446766b5f85 (diff) |
Curves: Add custom profile bevel support
This adds support for the same custom bevel profile widget used in
the bevel tool and modifier to the geometry generation for curves.
This is expecially useful for text and 2D curves with extrusion, as
it works much better than a weld & bevel modifier combination.
It can also be useful for adding quick detail to pipe-like objects.
The curve holds the CurveProfile struct and a new "Bevel Mode"
property decides which type of bevel to build, round, object, or
custom profile.
Although curves can already use another curve to make the bevel
geometry, this is a quicker way, and it also defines the profile of
just one corner of the bevel, so it isn't redundant. It's also nice
to have the same custom profile functionality wherever there is bevel.
Differential Revision: https://developer.blender.org/D8402
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/intern/curve.c | 15 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/curve_bevel.c | 156 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/curveprofile.c | 2 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_290.c | 12 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_curve_defaults.h | 1 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_curve_types.h | 13 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_curve.c | 65 |
7 files changed, 208 insertions, 56 deletions
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 74efa45cc73..24be6708785 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -50,6 +50,7 @@ #include "BKE_anim_data.h" #include "BKE_curve.h" +#include "BKE_curveprofile.h" #include "BKE_displist.h" #include "BKE_font.h" #include "BKE_idtype.h" @@ -95,6 +96,8 @@ static void curve_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int curve_dst->tb = MEM_dupallocN(curve_src->tb); curve_dst->batch_cache = NULL; + curve_dst->bevel_profile = BKE_curveprofile_copy(curve_src->bevel_profile); + if (curve_src->key && (flag & LIB_ID_COPY_SHAPEKEY)) { BKE_id_copy_ex(bmain, &curve_src->key->id, (ID **)&curve_dst->key, flag); /* XXX This is not nice, we need to make BKE_id_copy_ex fully re-entrant... */ @@ -116,6 +119,8 @@ static void curve_free_data(ID *id) BKE_curve_editNurb_free(curve); + BKE_curveprofile_free(curve->bevel_profile); + MEM_SAFE_FREE(curve->mat); MEM_SAFE_FREE(curve->str); MEM_SAFE_FREE(curve->strinfo); @@ -181,6 +186,10 @@ static void curve_blend_write(BlendWriter *writer, ID *id, const void *id_addres } } } + + if (cu->bevel_profile != NULL) { + BKE_curveprofile_blend_write(writer, cu->bevel_profile); + } } } } @@ -251,6 +260,11 @@ static void curve_blend_read_data(BlendDataReader *reader, ID *id) } } cu->texflag &= ~CU_AUTOSPACE_EVALUATED; + + BLO_read_data_address(reader, &cu->bevel_profile); + if (cu->bevel_profile != NULL) { + BKE_curveprofile_blend_read(reader, cu->bevel_profile); + } } static void curve_blend_read_lib(BlendLibReader *reader, ID *id) @@ -397,6 +411,7 @@ void BKE_curve_init(Curve *cu, const short curve_type) else if (cu->type == OB_SURF) { cu->resolv = 4; } + cu->bevel_profile = NULL; } Curve *BKE_curve_add(Main *bmain, const char *name, int type) diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c index edf5b82f822..911a98cb607 100644 --- a/source/blender/blenkernel/intern/curve_bevel.c +++ b/source/blender/blenkernel/intern/curve_bevel.c @@ -23,15 +23,18 @@ #include <string.h> +#include "BLI_alloca.h" #include "BLI_listbase.h" #include "BLI_math_base.h" #include "MEM_guardedalloc.h" #include "DNA_curve_types.h" +#include "DNA_curveprofile_types.h" #include "DNA_object_types.h" #include "BKE_curve.h" +#include "BKE_curveprofile.h" #include "BKE_displist.h" typedef enum CurveBevelFillType { @@ -53,6 +56,33 @@ static CurveBevelFillType curve_bevel_get_fill_type(const Curve *curve) return (curve->flag & CU_FRONT) ? FRONT : BACK; } +static void bevel_quarter_fill(Curve *curve, float *quarter_coords_x, float *quarter_coords_y) +{ + if (curve->bevel_mode == CU_BEV_MODE_ROUND) { + float angle = 0.0f; + const float dangle = (float)M_PI_2 / (curve->bevresol + 1); + for (int i = 0; i < curve->bevresol + 1; i++) { + quarter_coords_x[i] = (float)(cosf(angle) * (curve->ext2)); + quarter_coords_y[i] = (float)(sinf(angle) * (curve->ext2)); + angle += dangle; + } + } + else { + /* The curve profile evaluation should be done when the resolution is set. */ + BLI_assert(curve->bevel_profile->segments != NULL); + BLI_assert(curve->bevel_profile->segments_len == curve->bevresol + 1); + + /* If there aren't enough samples, the curveprofile won't + * sample the start vertex, so set it manually instead. */ + quarter_coords_x[0] = curve->ext2; + quarter_coords_y[0] = 0.0f; + for (int i = 1; i < curve->bevresol + 1; i++) { + quarter_coords_x[i] = (float)(curve->bevel_profile->segments[i].x * (curve->ext2)); + quarter_coords_y[i] = (float)(curve->bevel_profile->segments[i].y * (curve->ext2)); + } + } +} + static void curve_bevel_make_extrude_and_fill(Curve *cu, ListBase *disp, const bool use_extrude, @@ -60,10 +90,20 @@ static void curve_bevel_make_extrude_and_fill(Curve *cu, { DispList *dl = MEM_callocN(sizeof(DispList), __func__); + /* Calculate the profile of the bevel once to reuse it for each quarter. We will need + * to flip around the indices for every other section in order to build around the circle + * in a consistent direction. + * + * These should be small enough for stack allocations because the current limit + * for #Curve.bevresol is 32. */ + float *quarter_coords_x = alloca(sizeof(float) * (cu->bevresol + 1)); + float *quarter_coords_y = alloca(sizeof(float) * (cu->bevresol + 1)); + bevel_quarter_fill(cu, quarter_coords_x, quarter_coords_y); + int nr; if (fill_type == FULL) { /* The full loop. */ - nr = 4 * cu->bevresol + 6; + nr = 4 * cu->bevresol + (use_extrude ? 6 : 4); dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE; } else if (fill_type == HALF) { @@ -85,65 +125,69 @@ static void curve_bevel_make_extrude_and_fill(Curve *cu, dl->nr = nr; float *fp = dl->verts; - const float dangle = (float)M_PI_2 / (cu->bevresol + 1); - float angle = 0.0f; /* Build the back section. */ if (ELEM(fill_type, BACK, HALF, FULL)) { - angle = (float)M_PI_2 * 3.0f; - for (int i = 0; i < cu->bevresol + 2; i++) { + /* Add the bottom vertex. */ + fp[0] = 0.0f; + fp[1] = 0.0f; + fp[2] = -cu->ext1 - cu->ext2; + fp += 3; + + for (int i = cu->bevresol; i >= 0; i--) { fp[0] = 0.0f; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; - angle += dangle; + fp[1] = quarter_coords_x[i]; + fp[2] = -quarter_coords_y[i] - cu->ext1; fp += 3; } - if (use_extrude && fill_type == BACK) { - /* Add the extrusion if we're only building the back. */ - fp[0] = 0.0f; - fp[1] = cu->ext2; - fp[2] = cu->ext1; - } + } + + /* Add the extrusion if we're only building either the back or the front. */ + if (use_extrude && ELEM(fill_type, FRONT, BACK)) { + fp[0] = 0.0f; + fp[1] = cu->ext2; + fp[2] = (fill_type == FRONT) ? -cu->ext1 : cu->ext1; + fp += 3; } /* Build the front section. */ if (ELEM(fill_type, FRONT, HALF, FULL)) { - if (use_extrude && fill_type == FRONT) { - /* Add the extrusion if we're only building the front. */ - fp[0] = 0.0f; - fp[1] = cu->ext2; - fp[2] = -cu->ext1; - fp += 3; - } /* Don't duplicate the last back vertex. */ - angle = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? dangle : 0; - int front_len = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? cu->bevresol + 1 : - cu->bevresol + 2; - for (int i = 0; i < front_len; i++) { + const int front_start = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? 1 : 0; + for (int i = front_start; i < cu->bevresol + 1; i++) { fp[0] = 0.0f; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; - angle += dangle; + fp[1] = quarter_coords_x[i]; + fp[2] = quarter_coords_y[i] + cu->ext1; fp += 3; } + /* Add the top vertex. */ + fp[0] = 0.0f; + fp[1] = 0.0f; + fp[2] = cu->ext1 + cu->ext2; + fp += 3; } /* Build the other half only if we're building the full loop. */ if (fill_type == FULL) { - for (int i = 0; i < cu->bevresol + 1; i++) { + for (int i = cu->bevresol; i > 0; i--) { fp[0] = 0.0f; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; - angle += dangle; + fp[1] = -quarter_coords_x[i]; + fp[2] = quarter_coords_y[i] + cu->ext1; + fp += 3; + } + + if (use_extrude) { + /* Add the extrusion. */ + fp[0] = 0.0f; + fp[1] = -cu->ext2; + fp[2] = cu->ext1; fp += 3; } - angle = (float)M_PI; for (int i = 0; i < cu->bevresol + 1; i++) { fp[0] = 0.0f; - fp[1] = (float)(cosf(angle) * (cu->ext2)); - fp[2] = (float)(sinf(angle) * (cu->ext2)) - cu->ext1; - angle += dangle; + fp[1] = -quarter_coords_x[i]; + fp[2] = -quarter_coords_y[i] - cu->ext1; fp += 3; } } @@ -193,6 +237,9 @@ static void curve_bevel_make_only_extrude(Curve *cu, ListBase *disp) static void curve_bevel_make_from_object(Curve *cu, ListBase *disp) { + if (cu->bevobj == NULL) { + return; + } if (cu->bevobj->type != OB_CURVE) { return; } @@ -244,29 +291,30 @@ void BKE_curve_bevel_make(Object *ob, ListBase *disp) { Curve *curve = ob->data; - const bool use_extrude = curve->ext1 != 0.0f; - const bool use_bevel = curve->ext2 != 0.0f; - BLI_listbase_clear(disp); - if (curve->bevobj) { - curve_bevel_make_from_object(curve, disp); - } - else if (!(use_extrude || use_bevel)) { - /* Pass. */ - } - else if (use_extrude && !use_bevel) { - curve_bevel_make_only_extrude(curve, disp); + if (curve->bevel_mode == CU_BEV_MODE_OBJECT) { + if (curve->bevobj != NULL) { + curve_bevel_make_from_object(curve, disp); + } } else { - CurveBevelFillType fill_type = curve_bevel_get_fill_type(curve); - - if (!use_extrude && fill_type == FULL) { - curve_bevel_make_full_circle(curve, disp); + const bool use_extrude = curve->ext1 != 0.0f; + const bool use_bevel = curve->ext2 != 0.0f; + /* Pass. */ + if (use_extrude && !use_bevel) { + curve_bevel_make_only_extrude(curve, disp); } - else { - /* The general case for nonzero extrusion or an incomplete loop. */ - curve_bevel_make_extrude_and_fill(curve, disp, use_extrude, fill_type); + else if (use_extrude || use_bevel) { + CurveBevelFillType fill_type = curve_bevel_get_fill_type(curve); + + if (!use_extrude && fill_type == FULL && curve->bevel_mode == CU_BEV_MODE_ROUND) { + curve_bevel_make_full_circle(curve, disp); + } + else { + /* The general case for nonzero extrusion or an incomplete loop. */ + curve_bevel_make_extrude_and_fill(curve, disp, use_extrude, fill_type); + } } } } diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.c index f52619f8c3a..b7017253be1 100644 --- a/source/blender/blenkernel/intern/curveprofile.c +++ b/source/blender/blenkernel/intern/curveprofile.c @@ -1122,4 +1122,6 @@ void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurvePro for (int i = 0; i < profile->path_len; i++) { profile->path[i].profile = profile; } + + BKE_curveprofile_init(profile, profile->segments_len); } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 064b6139fde..63c3140c084 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -694,5 +694,17 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Add custom profile and bevel mode to curve bevels. */ + if (!DNA_struct_elem_find(fd->filesdna, "Curve", "char", "bevel_mode")) { + LISTBASE_FOREACH (Curve *, curve, &bmain->curves) { + if (curve->bevobj != NULL) { + curve->bevel_mode = CU_BEV_MODE_OBJECT; + } + else { + curve->bevel_mode = CU_BEV_MODE_ROUND; + } + } + } } } diff --git a/source/blender/makesdna/DNA_curve_defaults.h b/source/blender/makesdna/DNA_curve_defaults.h index 0cb3960dbd7..07dd0aeb08d 100644 --- a/source/blender/makesdna/DNA_curve_defaults.h +++ b/source/blender/makesdna/DNA_curve_defaults.h @@ -49,6 +49,7 @@ .bevfac1_mapping = CU_BEVFAC_MAP_RESOLU, \ .bevfac2_mapping = CU_BEVFAC_MAP_RESOLU, \ .bevresol = 4, \ + .bevel_mode = CU_BEV_MODE_ROUND, \ } /** \} */ diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 8d5b5602c2e..2f362034b78 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -31,6 +31,7 @@ #define MAXTEXTBOX 256 /* used in readfile.c and editfont.c */ struct AnimData; +struct CurveProfile; struct EditFont; struct GHash; struct Ipo; @@ -232,6 +233,8 @@ typedef struct Curve { struct Key *key; struct Material **mat; + struct CurveProfile *bevel_profile; + /* texture space, copied as one block in editobject.c */ float loc[3]; float size[3]; @@ -261,7 +264,8 @@ typedef struct Curve { char overflow; char spacemode, align_y; - char _pad[3]; + char bevel_mode; + char _pad[2]; /* font part */ short lines; @@ -385,6 +389,13 @@ enum { CU_ALIGN_Y_BOTTOM = 4, }; +/* Curve.bevel_mode */ +enum { + CU_BEV_MODE_ROUND = 0, + CU_BEV_MODE_OBJECT = 1, + CU_BEV_MODE_CURVE_PROFILE = 2, +}; + /* Curve.overflow. */ enum { CU_OVERFLOW_NONE = 0, diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 1768d79fe8f..854c8757d4d 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -167,6 +167,7 @@ static const EnumPropertyItem curve2d_fill_mode_items[] = { # include "DNA_object_types.h" # include "BKE_curve.h" +# include "BKE_curveprofile.h" # include "BKE_main.h" # include "DEG_depsgraph.h" @@ -463,6 +464,35 @@ static void rna_Curve_bevelObject_set(PointerRNA *ptr, } } +/** + * Special update function for setting the number of segments of the curve + * that also resamples the segments in the custom profile. + */ +static void rna_Curve_bevel_resolution_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + Curve *cu = (Curve *)ptr->data; + + if (cu->bevel_mode == CU_BEV_MODE_CURVE_PROFILE) { + BKE_curveprofile_init(cu->bevel_profile, cu->bevresol + 1); + } + + rna_Curve_update_data(bmain, scene, ptr); +} + +static void rna_Curve_bevel_mode_set(PointerRNA *ptr, int value) +{ + Curve *cu = (Curve *)ptr->owner_id; + + if (value == CU_BEV_MODE_CURVE_PROFILE) { + if (cu->bevel_profile == NULL) { + cu->bevel_profile = BKE_curveprofile_add(PROF_PRESET_LINE); + BKE_curveprofile_init(cu->bevel_profile, cu->bevresol + 1); + } + } + + cu->bevel_mode = value; +} + static bool rna_Curve_otherObject_poll(PointerRNA *ptr, PointerRNA value) { Curve *cu = (Curve *)ptr->owner_id; @@ -1527,6 +1557,25 @@ static void rna_def_curve(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem bevel_mode_items[] = { + {CU_BEV_MODE_ROUND, + "ROUND", + 0, + "Round", + "Use circle for the section of the curve's bevel geometry"}, + {CU_BEV_MODE_OBJECT, + "OBJECT", + 0, + "Object", + "Use an object for the section of the curve's bevel goemetry segment"}, + {CU_BEV_MODE_CURVE_PROFILE, + "PROFILE", + 0, + "Profile", + "Use a custom profile for each quarter of curve's bevel geometry"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "Curve", "ID"); RNA_def_struct_ui_text(srna, "Curve", "Curve data-block storing curves, splines and NURBS"); RNA_def_struct_ui_icon(srna, ICON_CURVE_DATA); @@ -1559,6 +1608,20 @@ static void rna_def_curve(BlenderRNA *brna) rna_def_path(brna, srna); + prop = RNA_def_property(srna, "bevel_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "bevel_mode"); + RNA_def_property_enum_items(prop, bevel_mode_items); + RNA_def_property_ui_text( + prop, "Bevel Mode", "Determine how to build the curve's bevel geometry"); + RNA_def_property_enum_funcs(prop, NULL, "rna_Curve_bevel_mode_set", NULL); + RNA_def_property_update(prop, 0, "rna_Curve_update_data"); + + prop = RNA_def_property(srna, "bevel_profile", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "CurveProfile"); + RNA_def_property_pointer_sdna(prop, NULL, "bevel_profile"); + RNA_def_property_ui_text(prop, "Custom Profile Path", "The path for the curve's custom profile"); + RNA_def_property_update(prop, 0, "rna_Curve_update_data"); + /* Number values */ prop = RNA_def_property(srna, "bevel_resolution", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "bevresol"); @@ -1568,7 +1631,7 @@ static void rna_def_curve(BlenderRNA *brna) prop, "Bevel Resolution", "Bevel resolution when depth is non-zero and no specific bevel object has been defined"); - RNA_def_property_update(prop, 0, "rna_Curve_update_data"); + RNA_def_property_update(prop, 0, "rna_Curve_bevel_resolution_update"); prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_NONE | PROP_UNIT_LENGTH); RNA_def_property_float_sdna(prop, NULL, "width"); |