GPencil: Curvature support for length modifier
Extend the stroke following an approximated circluar/helical curve. This can be used as an effect for lineart or on its own as helix generator. Reviewed By: Sebastian Parborg (zeddb), Hans Goudey (HooglyBoogly), YimingWu (NicksBest), Antonio Vazquez (antoniov), Henrik Dick (weasel) Differential Revision: https://developer.blender.org/D11668 # 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交 # 说明将会终止提交。 # # 位于分支 master # 您的分支与上游分支 'origin/master' 一致。 # # 要提交的变更: # 修改: source/blender/blenkernel/BKE_gpencil_geom.h # 修改: source/blender/blenkernel/intern/gpencil_geom.cc # 修改: source/blender/gpencil_modifiers/intern/MOD_gpencillength.c # 修改: source/blender/makesdna/DNA_gpencil_modifier_defaults.h # 修改: source/blender/makesdna/DNA_gpencil_modifier_types.h # 修改: source/blender/makesrna/intern/rna_gpencil_modifier.c #
This commit is contained in:
parent
84f98e28e3
commit
2e6a735385
|
@ -114,7 +114,12 @@ void BKE_gpencil_dissolve_points(struct bGPdata *gpd,
|
|||
bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps,
|
||||
const float dist,
|
||||
const float overshoot_fac,
|
||||
const short mode);
|
||||
const short mode,
|
||||
const bool follow_curvature,
|
||||
const int extra_point_count,
|
||||
const float segment_influence,
|
||||
const float max_angle,
|
||||
const bool invert_curvature);
|
||||
bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps,
|
||||
const int index_from,
|
||||
const int index_to);
|
||||
|
|
|
@ -540,65 +540,242 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Give extra stroke points before and after the original tip points.
|
||||
* \param gps: Target stroke
|
||||
* \param count_before: how many extra points to be added before a stroke
|
||||
* \param count_after: how many extra points to be added after a stroke
|
||||
*/
|
||||
static bool BKE_gpencil_stroke_extra_points(bGPDstroke *gps,
|
||||
const int count_before,
|
||||
const int count_after)
|
||||
{
|
||||
bGPDspoint *pts = gps->points;
|
||||
|
||||
BLI_assert(count_before >= 0);
|
||||
BLI_assert(count_after >= 0);
|
||||
if (!count_before && !count_after) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int new_count = count_before + count_after + gps->totpoints;
|
||||
|
||||
bGPDspoint *new_pts = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, __func__);
|
||||
|
||||
for (int i = 0; i < count_before; i++) {
|
||||
memcpy(&new_pts[i], &pts[0], sizeof(bGPDspoint));
|
||||
}
|
||||
memcpy(&new_pts[count_before], pts, sizeof(bGPDspoint) * gps->totpoints);
|
||||
for (int i = new_count - count_after; i < new_count; i++) {
|
||||
memcpy(&new_pts[i], &pts[gps->totpoints - 1], sizeof(bGPDspoint));
|
||||
}
|
||||
|
||||
if (gps->dvert) {
|
||||
MDeformVert *new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count, __func__);
|
||||
|
||||
for (int i = 0; i < new_count; i++) {
|
||||
MDeformVert *dv = &gps->dvert[CLAMPIS(i - count_before, 0, gps->totpoints - 1)];
|
||||
int inew = i;
|
||||
new_dv[inew].flag = dv->flag;
|
||||
new_dv[inew].totweight = dv->totweight;
|
||||
new_dv[inew].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight,
|
||||
__func__);
|
||||
memcpy(new_dv[inew].dw, dv->dw, sizeof(MDeformWeight) * dv->totweight);
|
||||
}
|
||||
BKE_gpencil_free_stroke_weights(gps);
|
||||
MEM_freeN(gps->dvert);
|
||||
gps->dvert = new_dv;
|
||||
}
|
||||
|
||||
MEM_freeN(gps->points);
|
||||
gps->points = new_pts;
|
||||
gps->totpoints = new_count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backbone stretch similar to Freestyle.
|
||||
* \param gps: Stroke to sample.
|
||||
* \param dist: Distance of one segment.
|
||||
* \param overshoot_fac: How exact is the follow curve algorithm.
|
||||
* \param dist: Length of the added section.
|
||||
* \param overshoot_fac: Relative length of the curve which is used to determine the extension.
|
||||
* \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End)
|
||||
* \param follow_curvature: True for appproximating curvature of given overshoot.
|
||||
* \param extra_point_count: When follow_curvature is true, use this amount of extra points
|
||||
*/
|
||||
bool BKE_gpencil_stroke_stretch(bGPDstroke *gps,
|
||||
const float dist,
|
||||
const float overshoot_fac,
|
||||
const short mode)
|
||||
const short mode,
|
||||
const bool follow_curvature,
|
||||
const int extra_point_count,
|
||||
const float segment_influence,
|
||||
const float max_angle,
|
||||
const bool invert_curvature)
|
||||
{
|
||||
#define BOTH 0
|
||||
#define START 1
|
||||
#define END 2
|
||||
|
||||
bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt;
|
||||
int i;
|
||||
float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac);
|
||||
const bool do_start = ELEM(mode, BOTH, START);
|
||||
const bool do_end = ELEM(mode, BOTH, END);
|
||||
float used_percent_length = overshoot_fac;
|
||||
CLAMP(used_percent_length, 1e-4f, 1.0f);
|
||||
if (!isfinite(used_percent_length)) {
|
||||
/* #used_percent_length must always be finite, otherwise a segfault occurs.
|
||||
* Since this function should never segfault, set #used_percent_length to a safe fallback. */
|
||||
/* NOTE: This fallback is used if gps->totpoints == 2, see MOD_gpencillength.c */
|
||||
used_percent_length = 0.1f;
|
||||
}
|
||||
|
||||
if (gps->totpoints < 2 || dist < FLT_EPSILON) {
|
||||
if (gps->totpoints <= 1 || dist < FLT_EPSILON || extra_point_count <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
last_pt = &pt[gps->totpoints - 1];
|
||||
second_last = &pt[gps->totpoints - 2];
|
||||
next_pt = &pt[1];
|
||||
/* NOTE: When it's just a straight line, we don't need to do the curvature stuff. */
|
||||
if (!follow_curvature || gps->totpoints <= 2) {
|
||||
/* Not following curvature, just straight line. */
|
||||
/* NOTE: #overshoot_point_param can not be zero. */
|
||||
float overshoot_point_param = used_percent_length * (gps->totpoints - 1);
|
||||
float result[3];
|
||||
|
||||
if (mode == BOTH || mode == START) {
|
||||
float len1 = 0.0f;
|
||||
i = 1;
|
||||
while (len1 < threshold && gps->totpoints > i) {
|
||||
next_pt = &pt[i];
|
||||
len1 = len_v3v3(&next_pt->x, &pt->x);
|
||||
i++;
|
||||
}
|
||||
float extend1 = (len1 + dist) / len1;
|
||||
float result1[3];
|
||||
|
||||
interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1);
|
||||
copy_v3_v3(&pt->x, result1);
|
||||
}
|
||||
|
||||
if (mode == BOTH || mode == END) {
|
||||
float len2 = 0.0f;
|
||||
i = 2;
|
||||
while (len2 < threshold && gps->totpoints >= i) {
|
||||
second_last = &pt[gps->totpoints - i];
|
||||
len2 = len_v3v3(&last_pt->x, &second_last->x);
|
||||
i++;
|
||||
if (do_start) {
|
||||
int index1 = floor(overshoot_point_param);
|
||||
int index2 = ceil(overshoot_point_param);
|
||||
interp_v3_v3v3(result,
|
||||
&gps->points[index1].x,
|
||||
&gps->points[index2].x,
|
||||
fmodf(overshoot_point_param, 1.0f));
|
||||
sub_v3_v3(result, &gps->points[0].x);
|
||||
if (UNLIKELY(is_zero_v3(result))) {
|
||||
sub_v3_v3v3(result, &gps->points[1].x, &gps->points[0].x);
|
||||
}
|
||||
madd_v3_v3fl(&gps->points[0].x, result, -dist / len_v3(result));
|
||||
}
|
||||
|
||||
float extend2 = (len2 + dist) / len2;
|
||||
float result2[3];
|
||||
interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2);
|
||||
|
||||
copy_v3_v3(&last_pt->x, result2);
|
||||
if (do_end) {
|
||||
int index1 = gps->totpoints - 1 - floor(overshoot_point_param);
|
||||
int index2 = gps->totpoints - 1 - ceil(overshoot_point_param);
|
||||
interp_v3_v3v3(result,
|
||||
&gps->points[index1].x,
|
||||
&gps->points[index2].x,
|
||||
fmodf(overshoot_point_param, 1.0f));
|
||||
sub_v3_v3(result, &gps->points[gps->totpoints - 1].x);
|
||||
if (UNLIKELY(is_zero_v3(result))) {
|
||||
sub_v3_v3v3(
|
||||
result, &gps->points[gps->totpoints - 2].x, &gps->points[gps->totpoints - 1].x);
|
||||
}
|
||||
madd_v3_v3fl(&gps->points[gps->totpoints - 1].x, result, -dist / len_v3(result));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Curvature calculation. */
|
||||
|
||||
/* First allocate the new stroke size. */
|
||||
const int first_old_index = do_start ? extra_point_count : 0;
|
||||
const int last_old_index = gps->totpoints - 1 + first_old_index;
|
||||
const int orig_totpoints = gps->totpoints;
|
||||
BKE_gpencil_stroke_extra_points(gps, first_old_index, do_end ? extra_point_count : 0);
|
||||
|
||||
/* The fractional amount of points to query when calculating the average curvature of the
|
||||
* strokes. */
|
||||
const float overshoot_parameter = used_percent_length * (orig_totpoints - 2);
|
||||
int overshoot_pointcount = ceil(overshoot_parameter);
|
||||
CLAMP(overshoot_pointcount, 1, orig_totpoints - 2);
|
||||
|
||||
/* Do for both sides without code duplication. */
|
||||
float no[3], vec1[3], vec2[3], total_angle[3];
|
||||
for (int k = 0; k < 2; k++) {
|
||||
if ((k == 0 && !do_start) || (k == 1 && !do_end)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int start_i = k == 0 ? first_old_index :
|
||||
last_old_index; // first_old_index, last_old_index
|
||||
const int dir_i = 1 - k * 2; // 1, -1
|
||||
|
||||
sub_v3_v3v3(vec1, &gps->points[start_i + dir_i].x, &gps->points[start_i].x);
|
||||
zero_v3(total_angle);
|
||||
float segment_length = normalize_v3(vec1);
|
||||
float overshoot_length = 0.0f;
|
||||
|
||||
/* Accumulate rotation angle and length. */
|
||||
int j = 0;
|
||||
for (int i = start_i; j < overshoot_pointcount; i += dir_i, j++) {
|
||||
/* Don't fully add last segment to get continuity in overshoot_fac. */
|
||||
float fac = fmin(overshoot_parameter - j, 1.0f);
|
||||
|
||||
/* Read segments. */
|
||||
copy_v3_v3(vec2, vec1);
|
||||
sub_v3_v3v3(vec1, &gps->points[i + dir_i * 2].x, &gps->points[i + dir_i].x);
|
||||
const float len = normalize_v3(vec1);
|
||||
float angle = angle_normalized_v3v3(vec1, vec2) * fac;
|
||||
|
||||
/* Add half of both adjacent legs of the current angle. */
|
||||
const float added_len = (segment_length + len) * 0.5f * fac;
|
||||
overshoot_length += added_len;
|
||||
segment_length = len;
|
||||
|
||||
if (angle > max_angle) {
|
||||
continue;
|
||||
}
|
||||
if (angle > M_PI * 0.995f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
angle *= powf(added_len, segment_influence);
|
||||
|
||||
cross_v3_v3v3(no, vec1, vec2);
|
||||
normalize_v3_length(no, angle);
|
||||
add_v3_v3(total_angle, no);
|
||||
}
|
||||
|
||||
if (UNLIKELY(overshoot_length == 0.0f)) {
|
||||
/* Don't do a proper extension if the used points are all in the same position. */
|
||||
continue;
|
||||
}
|
||||
|
||||
sub_v3_v3v3(vec1, &gps->points[start_i].x, &gps->points[start_i + dir_i].x);
|
||||
/* In general curvature = 1/radius. For the case without the
|
||||
* weights introduced by #segment_influence, the calculation is
|
||||
* curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length */
|
||||
float curvature = normalize_v3(total_angle) / overshoot_length;
|
||||
/* Compensate for the weights powf(added_len, segment_influence). */
|
||||
curvature /= powf(overshoot_length / fminf(overshoot_parameter, (float)j), segment_influence);
|
||||
if (invert_curvature) {
|
||||
curvature = -curvature;
|
||||
}
|
||||
const float angle_step = curvature * dist / extra_point_count;
|
||||
float step_length = dist / extra_point_count;
|
||||
if (fabsf(angle_step) > FLT_EPSILON) {
|
||||
/* Make a direct step length from the assigned arc step length. */
|
||||
step_length *= sin(angle_step * 0.5f) / (angle_step * 0.5f);
|
||||
}
|
||||
else {
|
||||
zero_v3(total_angle);
|
||||
}
|
||||
const float prev_length = normalize_v3_length(vec1, step_length);
|
||||
|
||||
/* Build rotation matrix here to get best performance. */
|
||||
float rot[3][3];
|
||||
float q[4];
|
||||
axis_angle_to_quat(q, total_angle, angle_step);
|
||||
quat_to_mat3(rot, q);
|
||||
|
||||
/* Rotate the starting direction to account for change in edge lengths. */
|
||||
axis_angle_to_quat(q,
|
||||
total_angle,
|
||||
fmaxf(0.0f, 1.0f - fabs(segment_influence)) *
|
||||
(curvature * prev_length - angle_step) / 2.0f);
|
||||
mul_qt_v3(q, vec1);
|
||||
|
||||
/* Now iteratively accumulate the segments with a rotating added direction. */
|
||||
for (int i = start_i - dir_i, j = 0; j < extra_point_count; i -= dir_i, j++) {
|
||||
mul_v3_m3v3(vec1, rot, vec1);
|
||||
add_v3_v3v3(&gps->points[i].x, vec1, &gps->points[i + dir_i].x);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -749,6 +926,7 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo
|
|||
|
||||
second_last = &pt[gps->totpoints - 2];
|
||||
|
||||
float len;
|
||||
float len1, cut_len1;
|
||||
float len2, cut_len2;
|
||||
len1 = len2 = cut_len1 = cut_len2 = 0.0f;
|
||||
|
@ -759,11 +937,13 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo
|
|||
i = 0;
|
||||
index_end = gps->totpoints - 1;
|
||||
while (len1 < dist && gps->totpoints > i + 1) {
|
||||
len1 += len_v3v3(&pt[i].x, &pt[i + 1].x);
|
||||
len = len_v3v3(&pt[i].x, &pt[i + 1].x);
|
||||
len1 += len;
|
||||
cut_len1 = len1 - dist;
|
||||
i++;
|
||||
}
|
||||
index_start = i - 1;
|
||||
interp_v3_v3v3(&pt[index_start].x, &pt[index_start + 1].x, &pt[index_start].x, cut_len1 / len);
|
||||
}
|
||||
|
||||
if (mode == END) {
|
||||
|
@ -771,18 +951,20 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo
|
|||
i = 2;
|
||||
while (len2 < dist && gps->totpoints >= i) {
|
||||
second_last = &pt[gps->totpoints - i];
|
||||
len2 += len_v3v3(&second_last[1].x, &second_last->x);
|
||||
len = len_v3v3(&second_last[1].x, &second_last->x);
|
||||
len2 += len;
|
||||
cut_len2 = len2 - dist;
|
||||
i++;
|
||||
}
|
||||
index_end = gps->totpoints - i + 2;
|
||||
interp_v3_v3v3(&pt[index_end].x, &pt[index_end - 1].x, &pt[index_end].x, cut_len2 / len);
|
||||
}
|
||||
|
||||
if (index_end <= index_start) {
|
||||
index_start = index_end = 0; /* empty stroke */
|
||||
}
|
||||
|
||||
if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) {
|
||||
if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < 0)) {
|
||||
index_start = index_end = 0; /* no length left to cut */
|
||||
}
|
||||
|
||||
|
|
|
@ -72,9 +72,14 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
|
|||
}
|
||||
|
||||
static bool gpencil_modify_stroke(bGPDstroke *gps,
|
||||
float length,
|
||||
const float length,
|
||||
const float overshoot_fac,
|
||||
const short len_mode)
|
||||
const short len_mode,
|
||||
const bool use_curvature,
|
||||
const int extra_point_count,
|
||||
const float segment_influence,
|
||||
const float max_angle,
|
||||
const bool invert_curvature)
|
||||
{
|
||||
bool changed = false;
|
||||
if (length == 0.0f) {
|
||||
|
@ -82,10 +87,18 @@ static bool gpencil_modify_stroke(bGPDstroke *gps,
|
|||
}
|
||||
|
||||
if (length > 0.0f) {
|
||||
BKE_gpencil_stroke_stretch(gps, length, overshoot_fac, len_mode);
|
||||
changed = BKE_gpencil_stroke_stretch(gps,
|
||||
length,
|
||||
overshoot_fac,
|
||||
len_mode,
|
||||
use_curvature,
|
||||
extra_point_count,
|
||||
segment_influence,
|
||||
max_angle,
|
||||
invert_curvature);
|
||||
}
|
||||
else {
|
||||
changed |= BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode);
|
||||
changed = BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode);
|
||||
}
|
||||
|
||||
return changed;
|
||||
|
@ -96,12 +109,51 @@ static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke
|
|||
bool changed = false;
|
||||
const float len = (lmd->mode == GP_LENGTH_ABSOLUTE) ? 1.0f :
|
||||
BKE_gpencil_stroke_length(gps, true);
|
||||
const int totpoints = gps->totpoints;
|
||||
if (len < FLT_EPSILON) {
|
||||
return;
|
||||
}
|
||||
|
||||
changed |= gpencil_modify_stroke(gps, len * lmd->start_fac, lmd->overshoot_fac, 1);
|
||||
changed |= gpencil_modify_stroke(gps, len * lmd->end_fac, lmd->overshoot_fac, 2);
|
||||
/* Always do the stretching first since it might depend on points which could be deleted by the
|
||||
* shrink. */
|
||||
float first_fac = lmd->start_fac;
|
||||
int first_mode = 1;
|
||||
float second_fac = lmd->end_fac;
|
||||
int second_mode = 2;
|
||||
if (first_fac < 0) {
|
||||
SWAP(float, first_fac, second_fac);
|
||||
SWAP(int, first_mode, second_mode);
|
||||
}
|
||||
|
||||
const int first_extra_point_count = ceil(first_fac * lmd->point_density);
|
||||
const int second_extra_point_count = ceil(second_fac * lmd->point_density);
|
||||
|
||||
changed |= gpencil_modify_stroke(gps,
|
||||
len * first_fac,
|
||||
lmd->overshoot_fac,
|
||||
first_mode,
|
||||
lmd->flag & GP_LENGTH_USE_CURVATURE,
|
||||
first_extra_point_count,
|
||||
lmd->segment_influence,
|
||||
lmd->max_angle,
|
||||
lmd->flag & GP_LENGTH_INVERT_CURVATURE);
|
||||
/* HACK: The second #overshoot_fac needs to be adjusted because it is not
|
||||
* done in the same stretch call, because it can have a different length.
|
||||
* The adjustment needs to be stable when
|
||||
* ceil(overshoot_fac*(gps->totpoints - 2)) is used in stretch and never
|
||||
* produce a result highter than totpoints - 2. */
|
||||
const float second_overshoot_fac = lmd->overshoot_fac * (totpoints - 2) /
|
||||
((float)gps->totpoints - 2) *
|
||||
(1.0f - 0.1f / (totpoints - 1.0f));
|
||||
changed |= gpencil_modify_stroke(gps,
|
||||
len * second_fac,
|
||||
second_overshoot_fac,
|
||||
second_mode,
|
||||
lmd->flag & GP_LENGTH_USE_CURVATURE,
|
||||
second_extra_point_count,
|
||||
lmd->segment_influence,
|
||||
lmd->max_angle,
|
||||
lmd->flag & GP_LENGTH_INVERT_CURVATURE);
|
||||
|
||||
if (changed) {
|
||||
BKE_gpencil_stroke_geometry_update(gpd, gps);
|
||||
|
@ -117,20 +169,25 @@ static void deformStroke(GpencilModifierData *md,
|
|||
{
|
||||
bGPdata *gpd = ob->data;
|
||||
LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md;
|
||||
if (is_stroke_affected_by_modifier(ob,
|
||||
lmd->layername,
|
||||
lmd->material,
|
||||
lmd->pass_index,
|
||||
lmd->layer_pass,
|
||||
1,
|
||||
gpl,
|
||||
gps,
|
||||
lmd->flag & GP_LENGTH_INVERT_LAYER,
|
||||
lmd->flag & GP_LENGTH_INVERT_PASS,
|
||||
lmd->flag & GP_LENGTH_INVERT_LAYERPASS,
|
||||
lmd->flag & GP_LENGTH_INVERT_MATERIAL)) {
|
||||
applyLength(lmd, gpd, gps);
|
||||
if (!is_stroke_affected_by_modifier(ob,
|
||||
lmd->layername,
|
||||
lmd->material,
|
||||
lmd->pass_index,
|
||||
lmd->layer_pass,
|
||||
1,
|
||||
gpl,
|
||||
gps,
|
||||
lmd->flag & GP_LENGTH_INVERT_LAYER,
|
||||
lmd->flag & GP_LENGTH_INVERT_PASS,
|
||||
lmd->flag & GP_LENGTH_INVERT_LAYERPASS,
|
||||
lmd->flag & GP_LENGTH_INVERT_MATERIAL)) {
|
||||
return;
|
||||
}
|
||||
if ((gps->flag & GP_STROKE_CYCLIC) != 0) {
|
||||
/* Don't affect cyclic strokes as they have no start/end. */
|
||||
return;
|
||||
}
|
||||
applyLength(lmd, gpd, gps);
|
||||
}
|
||||
|
||||
static void bakeModifier(Main *UNUSED(bmain),
|
||||
|
@ -168,10 +225,16 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
|||
|
||||
uiLayout *col = uiLayoutColumn(layout, true);
|
||||
|
||||
uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE);
|
||||
uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE);
|
||||
if (RNA_enum_get(ptr, "mode") == GP_LENGTH_RELATIVE) {
|
||||
uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE);
|
||||
uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE);
|
||||
}
|
||||
else {
|
||||
uiItemR(col, ptr, "start_length", 0, IFACE_("Start"), ICON_NONE);
|
||||
uiItemR(col, ptr, "end_length", 0, IFACE_("End"), ICON_NONE);
|
||||
}
|
||||
|
||||
uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Overshoot"), ICON_NONE);
|
||||
uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Used Length"), ICON_NONE);
|
||||
|
||||
gpencil_modifier_panel_end(layout, ptr);
|
||||
}
|
||||
|
@ -181,10 +244,39 @@ static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
|
|||
gpencil_modifier_masking_panel_draw(panel, true, false);
|
||||
}
|
||||
|
||||
static void curvature_header_draw(const bContext *UNUSED(C), Panel *panel)
|
||||
{
|
||||
uiLayout *layout = panel->layout;
|
||||
|
||||
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
|
||||
|
||||
uiItemR(layout, ptr, "use_curvature", 0, IFACE_("Curvature"), ICON_NONE);
|
||||
}
|
||||
|
||||
static void curvature_panel_draw(const bContext *UNUSED(C), Panel *panel)
|
||||
{
|
||||
uiLayout *layout = panel->layout;
|
||||
|
||||
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
|
||||
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
|
||||
uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_curvature"));
|
||||
|
||||
uiItemR(col, ptr, "point_density", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "segment_influence", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "max_angle", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "invert_curvature", 0, IFACE_("Invert"), ICON_NONE);
|
||||
}
|
||||
|
||||
static void panelRegister(ARegionType *region_type)
|
||||
{
|
||||
PanelType *panel_type = gpencil_modifier_panel_register(
|
||||
region_type, eGpencilModifierType_Length, panel_draw);
|
||||
gpencil_modifier_subpanel_register(
|
||||
region_type, "curvature", "", curvature_header_draw, curvature_panel_draw, panel_type);
|
||||
gpencil_modifier_subpanel_register(
|
||||
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
|
||||
}
|
||||
|
|
|
@ -314,9 +314,13 @@
|
|||
{ \
|
||||
.start_fac = 0.1f,\
|
||||
.end_fac = 0.1f,\
|
||||
.overshoot_fac = 0.01f,\
|
||||
.overshoot_fac = 0.1f,\
|
||||
.pass_index = 0,\
|
||||
.material = NULL,\
|
||||
.flag = GP_LENGTH_USE_CURVATURE,\
|
||||
.point_density = 30.0f,\
|
||||
.segment_influence = 0.0f,\
|
||||
.max_angle = DEG2RAD(170.0f),\
|
||||
}
|
||||
|
||||
#define _DNA_DEFAULT_DashGpencilModifierData \
|
||||
|
|
|
@ -493,7 +493,10 @@ typedef struct LengthGpencilModifierData {
|
|||
float overshoot_fac;
|
||||
/** Modifier mode. */
|
||||
int mode;
|
||||
char _pad[4];
|
||||
/* Curvature parameters. */
|
||||
float point_density;
|
||||
float segment_influence;
|
||||
float max_angle;
|
||||
} LengthGpencilModifierData;
|
||||
|
||||
typedef enum eLengthGpencil_Flag {
|
||||
|
@ -501,6 +504,8 @@ typedef enum eLengthGpencil_Flag {
|
|||
GP_LENGTH_INVERT_PASS = (1 << 1),
|
||||
GP_LENGTH_INVERT_LAYERPASS = (1 << 2),
|
||||
GP_LENGTH_INVERT_MATERIAL = (1 << 3),
|
||||
GP_LENGTH_USE_CURVATURE = (1 << 4),
|
||||
GP_LENGTH_INVERT_CURVATURE = (1 << 5),
|
||||
} eLengthGpencil_Flag;
|
||||
|
||||
typedef enum eLengthGpencil_Type {
|
||||
|
|
|
@ -74,6 +74,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
|
|||
ICON_MOD_DASH,
|
||||
"Dot Dash",
|
||||
"Generate dot-dash styled strokes"},
|
||||
{eGpencilModifierType_Length,
|
||||
"GP_LENGTH",
|
||||
ICON_MOD_LENGTH,
|
||||
"Length",
|
||||
"Extend or shrink strokes"},
|
||||
{eGpencilModifierType_Lineart,
|
||||
"GP_LINEART",
|
||||
ICON_MOD_LINEART,
|
||||
|
@ -120,11 +125,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
|
|||
ICON_MOD_LATTICE,
|
||||
"Lattice",
|
||||
"Deform strokes using lattice"},
|
||||
{eGpencilModifierType_Length,
|
||||
"GP_LENGTH",
|
||||
ICON_MOD_LENGTH,
|
||||
"Length",
|
||||
"Extend or shrink strokes"},
|
||||
{eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"},
|
||||
{eGpencilModifierType_Offset,
|
||||
"GP_OFFSET",
|
||||
|
@ -3278,14 +3278,29 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna)
|
|||
|
||||
prop = RNA_def_property(srna, "start_factor", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "start_fac");
|
||||
RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1);
|
||||
RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment");
|
||||
RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Start Factor", "Added length to the start of each stroke relative to its length");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "end_factor", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "end_fac");
|
||||
RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1);
|
||||
RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment");
|
||||
RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "End Factor", "Added length to the end of each stroke relative to its length");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "start_length", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "start_fac");
|
||||
RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Start Factor", "Absolute added length to the start of each stroke");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "end_length", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "end_fac");
|
||||
RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3);
|
||||
RNA_def_property_ui_text(prop, "End Factor", "Absolute added length to the end of each stroke");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "overshoot_factor", PROP_FLOAT, PROP_FACTOR);
|
||||
|
@ -3293,8 +3308,8 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna)
|
|||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Overshoot Factor",
|
||||
"Defines how precise must follow the stroke trajectory for the overshoot extremes");
|
||||
"Used Length",
|
||||
"Defines what portion of the stroke is used for the calculation of the extension");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
|
||||
|
@ -3303,6 +3318,44 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Mode", "Mode to define length");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_curvature", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_USE_CURVATURE);
|
||||
RNA_def_property_ui_text(prop, "Use Curvature", "Follow the curvature of the stroke");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "invert_curvature", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_CURVATURE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Invert Curvature", "Invert the curvature of the stroke's extension");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "point_density", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_range(prop, 0.1f, 1000.0f);
|
||||
RNA_def_property_ui_range(prop, 0.1f, 1000.0f, 1.0f, 1);
|
||||
RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Point Density", "Multiplied by Start/End for the total added point count");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "segment_influence", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_range(prop, -2.0f, 3.0f);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 2);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Segment Influence",
|
||||
"Factor to determine how much the length of the individual segments "
|
||||
"should influence the final computed curvature. Higher factors makes "
|
||||
"small segments influence the overall curvature less");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "max_angle", PROP_FLOAT, PROP_ANGLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Filter Angle",
|
||||
"Ignore points on the stroke that deviate from their neighbors by more "
|
||||
"than this angle when determining the extrapolation shape");
|
||||
RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f));
|
||||
RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(179.5f), 10.0f, 1);
|
||||
RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "layername");
|
||||
RNA_def_property_ui_text(prop, "Layer", "Layer name");
|
||||
|
|
Loading…
Reference in New Issue