GPencil: New Envelope Modifier
This new modifier creates a shape known as envelope. It connects all points that are n points apart. There is also a mode which fits a single stroke to the envelope shape that is determined by that rule. For more details, refer to the patch. Reviewed By: NicksBest, antoniov, frogstomp, mendio Differential Revision: http://developer.blender.org/D14341
This commit is contained in:
@@ -37,6 +37,7 @@ set(SRC
|
|||||||
intern/MOD_gpencilbuild.c
|
intern/MOD_gpencilbuild.c
|
||||||
intern/MOD_gpencilcolor.c
|
intern/MOD_gpencilcolor.c
|
||||||
intern/MOD_gpencildash.c
|
intern/MOD_gpencildash.c
|
||||||
|
intern/MOD_gpencilenvelope.c
|
||||||
intern/MOD_gpencilhook.c
|
intern/MOD_gpencilhook.c
|
||||||
intern/MOD_gpencillattice.c
|
intern/MOD_gpencillattice.c
|
||||||
intern/MOD_gpencillength.c
|
intern/MOD_gpencillength.c
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_WeightAngle;
|
|||||||
extern GpencilModifierTypeInfo modifierType_Gpencil_Lineart;
|
extern GpencilModifierTypeInfo modifierType_Gpencil_Lineart;
|
||||||
extern GpencilModifierTypeInfo modifierType_Gpencil_Dash;
|
extern GpencilModifierTypeInfo modifierType_Gpencil_Dash;
|
||||||
extern GpencilModifierTypeInfo modifierType_Gpencil_Shrinkwrap;
|
extern GpencilModifierTypeInfo modifierType_Gpencil_Shrinkwrap;
|
||||||
|
extern GpencilModifierTypeInfo modifierType_Gpencil_Envelope;
|
||||||
|
|
||||||
/* MOD_gpencil_util.c */
|
/* MOD_gpencil_util.c */
|
||||||
void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]);
|
void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]);
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[])
|
|||||||
INIT_GP_TYPE(Lineart);
|
INIT_GP_TYPE(Lineart);
|
||||||
INIT_GP_TYPE(Dash);
|
INIT_GP_TYPE(Dash);
|
||||||
INIT_GP_TYPE(Shrinkwrap);
|
INIT_GP_TYPE(Shrinkwrap);
|
||||||
|
INIT_GP_TYPE(Envelope);
|
||||||
#undef INIT_GP_TYPE
|
#undef INIT_GP_TYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
629
source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
Normal file
629
source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
Normal file
@@ -0,0 +1,629 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* Copyright 2017 Blender Foundation. */
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \ingroup modifiers
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "BLI_listbase.h"
|
||||||
|
#include "BLI_math.h"
|
||||||
|
#include "BLI_math_geom.h"
|
||||||
|
#include "BLI_utildefines.h"
|
||||||
|
|
||||||
|
#include "BLT_translation.h"
|
||||||
|
|
||||||
|
#include "DNA_defaults.h"
|
||||||
|
#include "DNA_gpencil_modifier_types.h"
|
||||||
|
#include "DNA_gpencil_types.h"
|
||||||
|
#include "DNA_meshdata_types.h"
|
||||||
|
#include "DNA_object_types.h"
|
||||||
|
#include "DNA_scene_types.h"
|
||||||
|
#include "DNA_screen_types.h"
|
||||||
|
|
||||||
|
#include "BKE_colortools.h"
|
||||||
|
#include "BKE_context.h"
|
||||||
|
#include "BKE_deform.h"
|
||||||
|
#include "BKE_gpencil.h"
|
||||||
|
#include "BKE_gpencil_geom.h"
|
||||||
|
#include "BKE_gpencil_modifier.h"
|
||||||
|
#include "BKE_lib_query.h"
|
||||||
|
#include "BKE_modifier.h"
|
||||||
|
#include "BKE_screen.h"
|
||||||
|
|
||||||
|
#include "DEG_depsgraph.h"
|
||||||
|
#include "DEG_depsgraph_build.h"
|
||||||
|
#include "DEG_depsgraph_query.h"
|
||||||
|
|
||||||
|
#include "UI_interface.h"
|
||||||
|
#include "UI_resources.h"
|
||||||
|
|
||||||
|
#include "RNA_access.h"
|
||||||
|
|
||||||
|
#include "MOD_gpencil_modifiertypes.h"
|
||||||
|
#include "MOD_gpencil_ui_common.h"
|
||||||
|
#include "MOD_gpencil_util.h"
|
||||||
|
|
||||||
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
|
static void initData(GpencilModifierData *md)
|
||||||
|
{
|
||||||
|
EnvelopeGpencilModifierData *gpmd = (EnvelopeGpencilModifierData *)md;
|
||||||
|
|
||||||
|
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
|
||||||
|
|
||||||
|
MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(EnvelopeGpencilModifierData), modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
|
||||||
|
{
|
||||||
|
BKE_gpencil_modifier_copydata_generic(md, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float calc_min_radius_v3v3(float p1[3], float p2[3], float dir[3])
|
||||||
|
{
|
||||||
|
/* Use plane-conic-intersections to choose the maximal radius.
|
||||||
|
* The conic is deifned in 4D as f({x,y,z,t}) = x*x + y*y + z*z - t*t = 0
|
||||||
|
* Then a plane is defined parametrically as
|
||||||
|
* {p}(u, v) = {p1,0}*u + {p2,0}*(1-u) + {dir,1}*v with 0 <= u <= 1 and v >= 0
|
||||||
|
* Now compute the intersection point with the smallest t.
|
||||||
|
* To do so, compute the parameters u, v such that f(p(u, v)) = 0 and v is minimal.
|
||||||
|
* This can be done analytically and the solution is:
|
||||||
|
* u = -dot(p2,dir) / dot(p1-p2, dir) +/- sqrt((dot(p2,dir) / dot(p1-p2, dir))^2 -
|
||||||
|
* (2*dot(p1-p2,p2)*dot(p2,dir)-dot(p2,p2)*dot(p1-p2,dir))/(dot(p1-p2,dir)*dot(p1-p2,p1-p2)));
|
||||||
|
* v = ({p1}u + {p2}*(1-u))^2 / (2*(dot(p1,dir)*u + dot(p2,dir)*(1-u)));
|
||||||
|
*/
|
||||||
|
float diff[3];
|
||||||
|
float p1_dir = dot_v3v3(p1, dir);
|
||||||
|
float p2_dir = dot_v3v3(p2, dir);
|
||||||
|
float p2_sqr = len_squared_v3(p2);
|
||||||
|
float diff_dir = p1_dir - p2_dir;
|
||||||
|
float u = 0.5f;
|
||||||
|
if (diff_dir != 0.0f) {
|
||||||
|
float p = p2_dir / diff_dir;
|
||||||
|
sub_v3_v3v3(diff, p1, p2);
|
||||||
|
float diff_sqr = len_squared_v3(diff);
|
||||||
|
float diff_p2 = dot_v3v3(diff, p2);
|
||||||
|
float q = (2 * diff_p2 * p2_dir - p2_sqr * diff_dir) / (diff_dir * diff_sqr);
|
||||||
|
if (p * p - q >= 0) {
|
||||||
|
u = -p - sqrtf(p * p - q) * copysign(1.0f, p);
|
||||||
|
CLAMP(u, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
u = 0.5f - copysign(0.5f, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float p1_sqr = len_squared_v3(p1);
|
||||||
|
u = p1_sqr < p2_sqr ? 1.0f : 0.0f;
|
||||||
|
}
|
||||||
|
float p[3];
|
||||||
|
interp_v3_v3v3(p, p2, p1, u);
|
||||||
|
/* v is the determined minimal radius. In case p1 and p2 are the same, there is a
|
||||||
|
* simple proof for the following formula using the geometric mean theorem and Thales theorem. */
|
||||||
|
float v = len_squared_v3(p) / (2 * interpf(p1_dir, p2_dir, u));
|
||||||
|
if (v < 0 || !isfinite(v)) {
|
||||||
|
/* No limit to the radius from this segment. */
|
||||||
|
return 1e16f;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float calc_radius_limit(
|
||||||
|
bGPDstroke *gps, bGPDspoint *points, float dir[3], int spread, const int i)
|
||||||
|
{
|
||||||
|
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||||
|
bGPDspoint *pt = &points[i];
|
||||||
|
|
||||||
|
/* NOTE this part is the second performance critical part. Improvements are welcome. */
|
||||||
|
float radius_limit = 1e16f;
|
||||||
|
float p1[3], p2[3];
|
||||||
|
if (is_cyclic) {
|
||||||
|
if (gps->totpoints / 2 < spread) {
|
||||||
|
spread = gps->totpoints / 2;
|
||||||
|
}
|
||||||
|
const int start = i + gps->totpoints;
|
||||||
|
for (int j = -spread; j <= spread; j++) {
|
||||||
|
j += (j == 0);
|
||||||
|
const int i1 = (start + j) % gps->totpoints;
|
||||||
|
const int i2 = (start + j + (j > 0) - (j < 0)) % gps->totpoints;
|
||||||
|
sub_v3_v3v3(p1, &points[i1].x, &pt->x);
|
||||||
|
sub_v3_v3v3(p2, &points[i2].x, &pt->x);
|
||||||
|
float r = calc_min_radius_v3v3(p1, p2, dir);
|
||||||
|
radius_limit = min_ff(radius_limit, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const int start = max_ii(-spread, 1 - i);
|
||||||
|
const int end = min_ii(spread, gps->totpoints - 2 - i);
|
||||||
|
for (int j = start; j <= end; j++) {
|
||||||
|
if (j == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const int i1 = i + j;
|
||||||
|
const int i2 = i + j + (j > 0) - (j < 0);
|
||||||
|
sub_v3_v3v3(p1, &points[i1].x, &pt->x);
|
||||||
|
sub_v3_v3v3(p2, &points[i2].x, &pt->x);
|
||||||
|
float r = calc_min_radius_v3v3(p1, p2, dir);
|
||||||
|
radius_limit = min_ff(radius_limit, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return radius_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void apply_stroke_envelope(
|
||||||
|
bGPDstroke *gps, int spread, const int def_nr, const bool invert_vg, const float thickness)
|
||||||
|
{
|
||||||
|
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
|
||||||
|
if (is_cyclic) {
|
||||||
|
const int half = gps->totpoints / 2;
|
||||||
|
spread = abs(((spread + half) % gps->totpoints) - half);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
spread = min_ii(spread, gps->totpoints - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int spread_left = (spread + 2) / 2;
|
||||||
|
const int spread_right = (spread + 1) / 2;
|
||||||
|
|
||||||
|
/* Copy the point data. Only need positions, but extracting them
|
||||||
|
* is probably just as expensive as a full copy. */
|
||||||
|
bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
|
||||||
|
|
||||||
|
/* Deform the stroke to match the envelope shape. */
|
||||||
|
for (int i = 0; i < gps->totpoints; i++) {
|
||||||
|
MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
|
||||||
|
|
||||||
|
/* Verify in vertex group. */
|
||||||
|
float weight = get_modifier_point_weight(dvert, invert_vg, def_nr);
|
||||||
|
if (weight < 0.0f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index1 = i - spread_left;
|
||||||
|
int index2 = i + spread_right;
|
||||||
|
CLAMP(index1, 0, gps->totpoints - 1);
|
||||||
|
CLAMP(index2, 0, gps->totpoints - 1);
|
||||||
|
|
||||||
|
bGPDspoint *point = &gps->points[i];
|
||||||
|
point->pressure *= interpf(thickness, 1.0f, weight);
|
||||||
|
|
||||||
|
float closest[3];
|
||||||
|
float closest2[3];
|
||||||
|
copy_v3_v3(closest2, &point->x);
|
||||||
|
float dist = 0.0f;
|
||||||
|
float dist2 = 0.0f;
|
||||||
|
/* Create plane from point and neighbors and intersect that with the line. */
|
||||||
|
float v1[3], v2[3], plane_no[3];
|
||||||
|
sub_v3_v3v3(
|
||||||
|
v1,
|
||||||
|
&old_points[is_cyclic ? (i - 1 + gps->totpoints) % gps->totpoints : max_ii(0, i - 1)].x,
|
||||||
|
&old_points[i].x);
|
||||||
|
sub_v3_v3v3(
|
||||||
|
v2,
|
||||||
|
&old_points[is_cyclic ? (i + 1) % gps->totpoints : min_ii(gps->totpoints - 1, i + 1)].x,
|
||||||
|
&old_points[i].x);
|
||||||
|
normalize_v3(v1);
|
||||||
|
normalize_v3(v2);
|
||||||
|
sub_v3_v3v3(plane_no, v1, v2);
|
||||||
|
if (normalize_v3(plane_no) == 0.0f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Now find the intersections with the plane. */
|
||||||
|
/* NOTE this part is the first performance critical part. Improvements are welcome. */
|
||||||
|
float tmp_closest[3];
|
||||||
|
for (int j = -spread_right; j <= spread_left; j++) {
|
||||||
|
const int i1 = is_cyclic ? (i + j - spread_left + gps->totpoints) % gps->totpoints :
|
||||||
|
max_ii(0, i + j - spread_left);
|
||||||
|
const int i2 = is_cyclic ? (i + j + spread_right) % gps->totpoints :
|
||||||
|
min_ii(gps->totpoints - 1, i + j + spread_right);
|
||||||
|
/*bool side = dot_v3v3(&old_points[i1].x, plane_no) < dot_v3v3(plane_no, &old_points[i2].x);
|
||||||
|
if (side) {
|
||||||
|
continue;
|
||||||
|
}*/
|
||||||
|
float lambda = line_plane_factor_v3(
|
||||||
|
&point->x, plane_no, &old_points[i1].x, &old_points[i2].x);
|
||||||
|
if (lambda <= 0.0f || lambda >= 1.0f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
interp_v3_v3v3(tmp_closest, &old_points[i1].x, &old_points[i2].x, lambda);
|
||||||
|
|
||||||
|
float dir[3];
|
||||||
|
sub_v3_v3v3(dir, tmp_closest, &point->x);
|
||||||
|
float d = len_v3(dir);
|
||||||
|
/* Use a formula to find the diameter of the circle that would touch the line. */
|
||||||
|
float cos_angle = fabsf(dot_v3v3(plane_no, &old_points[i1].x) -
|
||||||
|
dot_v3v3(plane_no, &old_points[i2].x)) /
|
||||||
|
len_v3v3(&old_points[i1].x, &old_points[i2].x);
|
||||||
|
d *= 2 * cos_angle / (1 + cos_angle);
|
||||||
|
float to_closest[3];
|
||||||
|
sub_v3_v3v3(to_closest, closest, &point->x);
|
||||||
|
if (dist == 0.0f) {
|
||||||
|
dist = d;
|
||||||
|
copy_v3_v3(closest, tmp_closest);
|
||||||
|
}
|
||||||
|
else if (dot_v3v3(to_closest, dir) >= 0) {
|
||||||
|
if (d > dist) {
|
||||||
|
dist = d;
|
||||||
|
copy_v3_v3(closest, tmp_closest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (d > dist2) {
|
||||||
|
dist2 = d;
|
||||||
|
copy_v3_v3(closest2, tmp_closest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dist == 0.0f) {
|
||||||
|
copy_v3_v3(closest, &point->x);
|
||||||
|
}
|
||||||
|
if (dist2 == 0.0f) {
|
||||||
|
copy_v3_v3(closest2, &point->x);
|
||||||
|
}
|
||||||
|
dist = dist + dist2;
|
||||||
|
|
||||||
|
if (dist < FLT_EPSILON) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float use_dist = dist;
|
||||||
|
|
||||||
|
/* Apply radius limiting to not cross existing lines. */
|
||||||
|
float dir[3], new_center[3];
|
||||||
|
interp_v3_v3v3(new_center, closest2, closest, 0.5f);
|
||||||
|
sub_v3_v3v3(dir, new_center, &point->x);
|
||||||
|
if (normalize_v3(dir) != 0.0f && (is_cyclic || (i > 0 && i < gps->totpoints - 1))) {
|
||||||
|
const float max_radius = calc_radius_limit(gps, old_points, dir, spread, i);
|
||||||
|
use_dist = min_ff(use_dist, 2 * max_radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
float fac = use_dist * weight;
|
||||||
|
/* The 50 is an internal constant for the default pixel size. The result can be messed up if
|
||||||
|
* bGPdata.pixfactor is not default, but I think modifiers shouldn't access that. */
|
||||||
|
point->pressure += fac * 50.0f * GP_DEFAULT_PIX_FACTOR;
|
||||||
|
interp_v3_v3v3(&point->x, &point->x, new_center, fac / len_v3v3(closest, closest2));
|
||||||
|
}
|
||||||
|
|
||||||
|
MEM_freeN(old_points);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply envelope effect to the stroke.
|
||||||
|
*/
|
||||||
|
static void deformStroke(GpencilModifierData *md,
|
||||||
|
Depsgraph *UNUSED(depsgraph),
|
||||||
|
Object *ob,
|
||||||
|
bGPDlayer *gpl,
|
||||||
|
bGPDframe *UNUSED(gpf),
|
||||||
|
bGPDstroke *gps)
|
||||||
|
{
|
||||||
|
EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md;
|
||||||
|
if (mmd->mode != GP_ENVELOPE_DEFORM) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
|
||||||
|
|
||||||
|
if (!is_stroke_affected_by_modifier(ob,
|
||||||
|
mmd->layername,
|
||||||
|
mmd->material,
|
||||||
|
mmd->pass_index,
|
||||||
|
mmd->layer_pass,
|
||||||
|
3,
|
||||||
|
gpl,
|
||||||
|
gps,
|
||||||
|
mmd->flag & GP_ENVELOPE_INVERT_LAYER,
|
||||||
|
mmd->flag & GP_ENVELOPE_INVERT_PASS,
|
||||||
|
mmd->flag & GP_ENVELOPE_INVERT_LAYERPASS,
|
||||||
|
mmd->flag & GP_ENVELOPE_INVERT_MATERIAL)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mmd->spread <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_stroke_envelope(
|
||||||
|
gps, mmd->spread, def_nr, (mmd->flag & GP_ENVELOPE_INVERT_VGROUP) != 0, mmd->thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_stroke(Object *ob,
|
||||||
|
bGPDstroke *gps,
|
||||||
|
const int point_index,
|
||||||
|
const int connection_index,
|
||||||
|
const int size,
|
||||||
|
const int mat_nr,
|
||||||
|
const float thickness,
|
||||||
|
const float strength,
|
||||||
|
ListBase *results)
|
||||||
|
{
|
||||||
|
bGPdata *gpd = ob->data;
|
||||||
|
bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, size, gps->thickness);
|
||||||
|
|
||||||
|
const int size1 = size == 4 ? 2 : 1;
|
||||||
|
const int size2 = size - size1;
|
||||||
|
|
||||||
|
memcpy(&gps_dst->points[0], &gps->points[connection_index], size1 * sizeof(bGPDspoint));
|
||||||
|
memcpy(&gps_dst->points[size1], &gps->points[point_index], size2 * sizeof(bGPDspoint));
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
gps_dst->points[i].pressure *= thickness;
|
||||||
|
gps_dst->points[i].strength *= strength;
|
||||||
|
memset(&gps_dst->points[i].runtime, 0, sizeof(bGPDspoint_Runtime));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gps->dvert != NULL) {
|
||||||
|
gps_dst->dvert = MEM_malloc_arrayN(size, sizeof(MDeformVert), __func__);
|
||||||
|
BKE_defvert_array_copy(&gps_dst->dvert[0], &gps->dvert[connection_index], size1);
|
||||||
|
BKE_defvert_array_copy(&gps_dst->dvert[size1], &gps->dvert[point_index], size2);
|
||||||
|
}
|
||||||
|
|
||||||
|
BLI_addtail(results, gps_dst);
|
||||||
|
|
||||||
|
/* Calc geometry data. */
|
||||||
|
BKE_gpencil_stroke_geometry_update(gpd, gps_dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_stroke_cyclic(Object *ob,
|
||||||
|
bGPDstroke *gps,
|
||||||
|
const int point_index,
|
||||||
|
const int connection_index,
|
||||||
|
const int mat_nr,
|
||||||
|
const float thickness,
|
||||||
|
const float strength,
|
||||||
|
ListBase *results)
|
||||||
|
{
|
||||||
|
bGPdata *gpd = ob->data;
|
||||||
|
bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, 4, gps->thickness);
|
||||||
|
|
||||||
|
int connection_index2 = (connection_index + 1) % gps->totpoints;
|
||||||
|
int point_index2 = (point_index + 1) % gps->totpoints;
|
||||||
|
|
||||||
|
gps_dst->points[0] = gps->points[connection_index];
|
||||||
|
gps_dst->points[1] = gps->points[connection_index2];
|
||||||
|
gps_dst->points[2] = gps->points[point_index];
|
||||||
|
gps_dst->points[3] = gps->points[point_index2];
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
gps_dst->points[i].pressure *= thickness;
|
||||||
|
gps_dst->points[i].strength *= strength;
|
||||||
|
memset(&gps_dst->points[i].runtime, 0, sizeof(bGPDspoint_Runtime));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gps->dvert != NULL) {
|
||||||
|
gps_dst->dvert = MEM_malloc_arrayN(4, sizeof(MDeformVert), __func__);
|
||||||
|
BKE_defvert_array_copy(&gps_dst->dvert[0], &gps->dvert[connection_index], 1);
|
||||||
|
BKE_defvert_array_copy(&gps_dst->dvert[1], &gps->dvert[connection_index2], 1);
|
||||||
|
BKE_defvert_array_copy(&gps_dst->dvert[2], &gps->dvert[point_index], 1);
|
||||||
|
BKE_defvert_array_copy(&gps_dst->dvert[3], &gps->dvert[point_index2], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
BLI_addtail(results, gps_dst);
|
||||||
|
|
||||||
|
/* Calc geometry data. */
|
||||||
|
BKE_gpencil_stroke_geometry_update(gpd, gps_dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_stroke_simple(Object *ob,
|
||||||
|
bGPDstroke *gps,
|
||||||
|
const int point_index,
|
||||||
|
const int connection_index,
|
||||||
|
const int mat_nr,
|
||||||
|
const float thickness,
|
||||||
|
const float strength,
|
||||||
|
ListBase *results)
|
||||||
|
{
|
||||||
|
bGPdata *gpd = ob->data;
|
||||||
|
bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, 2, gps->thickness);
|
||||||
|
|
||||||
|
gps_dst->points[0] = gps->points[connection_index];
|
||||||
|
gps_dst->points[0].pressure *= thickness;
|
||||||
|
gps_dst->points[0].strength *= strength;
|
||||||
|
memset(&gps_dst->points[0].runtime, 0, sizeof(bGPDspoint_Runtime));
|
||||||
|
gps_dst->points[1] = gps->points[point_index];
|
||||||
|
gps_dst->points[1].pressure *= thickness;
|
||||||
|
gps_dst->points[1].strength *= strength;
|
||||||
|
memset(&gps_dst->points[1].runtime, 0, sizeof(bGPDspoint_Runtime));
|
||||||
|
|
||||||
|
if (gps->dvert != NULL) {
|
||||||
|
gps_dst->dvert = MEM_malloc_arrayN(2, sizeof(MDeformVert), __func__);
|
||||||
|
BKE_defvert_array_copy(&gps_dst->dvert[0], &gps->dvert[connection_index], 1);
|
||||||
|
BKE_defvert_array_copy(&gps_dst->dvert[1], &gps->dvert[point_index], 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
BLI_addtail(results, gps_dst);
|
||||||
|
|
||||||
|
/* Calc geometry data. */
|
||||||
|
BKE_gpencil_stroke_geometry_update(gpd, gps_dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf)
|
||||||
|
{
|
||||||
|
EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md;
|
||||||
|
ListBase duplicates = {0};
|
||||||
|
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
|
||||||
|
if (!is_stroke_affected_by_modifier(ob,
|
||||||
|
mmd->layername,
|
||||||
|
mmd->material,
|
||||||
|
mmd->pass_index,
|
||||||
|
mmd->layer_pass,
|
||||||
|
3,
|
||||||
|
gpl,
|
||||||
|
gps,
|
||||||
|
mmd->flag & GP_ENVELOPE_INVERT_LAYER,
|
||||||
|
mmd->flag & GP_ENVELOPE_INVERT_PASS,
|
||||||
|
mmd->flag & GP_ENVELOPE_INVERT_LAYERPASS,
|
||||||
|
mmd->flag & GP_ENVELOPE_INVERT_MATERIAL)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int mat_nr = mmd->mat_nr < 0 ? gps->mat_nr : min_ii(mmd->mat_nr, ob->totcol - 1);
|
||||||
|
if (mmd->mode == GP_ENVELOPE_FILLS) {
|
||||||
|
if (gps->flag & GP_STROKE_CYCLIC) {
|
||||||
|
for (int i = 0; i < gps->totpoints; i++) {
|
||||||
|
const int connection_index = (i + mmd->spread) % gps->totpoints;
|
||||||
|
add_stroke_cyclic(
|
||||||
|
ob, gps, i, connection_index, mat_nr, mmd->thickness, mmd->strength, &duplicates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 1; i < gps->totpoints - 1 && i < mmd->spread + 1; i++) {
|
||||||
|
add_stroke(ob, gps, i, 0, 3, mat_nr, mmd->thickness, mmd->strength, &duplicates);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < gps->totpoints - 1; i++) {
|
||||||
|
const int connection_index = min_ii(i + mmd->spread, gps->totpoints - 1);
|
||||||
|
const int size = i == gps->totpoints - 2 ? 2 :
|
||||||
|
connection_index < gps->totpoints - 1 ? 4 :
|
||||||
|
3;
|
||||||
|
add_stroke(ob,
|
||||||
|
gps,
|
||||||
|
i,
|
||||||
|
connection_index,
|
||||||
|
size,
|
||||||
|
mat_nr,
|
||||||
|
mmd->thickness,
|
||||||
|
mmd->strength,
|
||||||
|
&duplicates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BLI_remlink(&gpf->strokes, gps);
|
||||||
|
BKE_gpencil_free_stroke(gps);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BLI_assert(mmd->mode == GP_ENVELOPE_SEGMENTS);
|
||||||
|
if (gps->flag & GP_STROKE_CYCLIC) {
|
||||||
|
for (int i = 0; i < gps->totpoints; i++) {
|
||||||
|
const int connection_index = (i + 1 + mmd->spread) % gps->totpoints;
|
||||||
|
add_stroke_simple(
|
||||||
|
ob, gps, i, connection_index, mat_nr, mmd->thickness, mmd->strength, &duplicates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = -mmd->spread; i < gps->totpoints - 1; i++) {
|
||||||
|
const int connection_index = min_ii(i + 1 + mmd->spread, gps->totpoints - 1);
|
||||||
|
add_stroke_simple(ob,
|
||||||
|
gps,
|
||||||
|
max_ii(0, i),
|
||||||
|
connection_index,
|
||||||
|
mat_nr,
|
||||||
|
mmd->thickness,
|
||||||
|
mmd->strength,
|
||||||
|
&duplicates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!BLI_listbase_is_empty(&duplicates)) {
|
||||||
|
/* Add strokes to the start of the stroke list to ensure the new lines are drawn underneath the
|
||||||
|
* original line. */
|
||||||
|
BLI_movelisttolist_reverse(&gpf->strokes, &duplicates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply envelope effect to the strokes.
|
||||||
|
*/
|
||||||
|
static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob)
|
||||||
|
{
|
||||||
|
EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md;
|
||||||
|
if (mmd->mode == GP_ENVELOPE_DEFORM || mmd->spread <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
||||||
|
bGPdata *gpd = (bGPdata *)ob->data;
|
||||||
|
|
||||||
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
||||||
|
bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl);
|
||||||
|
if (gpf == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
generate_geometry(md, ob, gpl, gpf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bakeModifier(struct Main *UNUSED(bmain),
|
||||||
|
Depsgraph *depsgraph,
|
||||||
|
GpencilModifierData *md,
|
||||||
|
Object *ob)
|
||||||
|
{
|
||||||
|
EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md;
|
||||||
|
if (mmd->mode == GP_ENVELOPE_DEFORM) {
|
||||||
|
generic_bake_deform_stroke(depsgraph, md, ob, false, deformStroke);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bGPdata *gpd = ob->data;
|
||||||
|
|
||||||
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
||||||
|
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
|
||||||
|
generate_geometry(md, ob, gpl, gpf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
|
||||||
|
{
|
||||||
|
EnvelopeGpencilModifierData *mmd = (EnvelopeGpencilModifierData *)md;
|
||||||
|
|
||||||
|
walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
||||||
|
{
|
||||||
|
uiLayout *row;
|
||||||
|
uiLayout *layout = panel->layout;
|
||||||
|
|
||||||
|
PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL);
|
||||||
|
|
||||||
|
uiLayoutSetPropSep(layout, true);
|
||||||
|
|
||||||
|
uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE);
|
||||||
|
|
||||||
|
uiItemR(layout, ptr, "spread", 0, NULL, ICON_NONE);
|
||||||
|
uiItemR(layout, ptr, "thickness", 0, NULL, ICON_NONE);
|
||||||
|
|
||||||
|
const int mode = RNA_enum_get(ptr, "mode");
|
||||||
|
if (mode != GP_ENVELOPE_DEFORM) {
|
||||||
|
uiItemR(layout, ptr, "strength", 0, NULL, ICON_NONE);
|
||||||
|
uiItemR(layout, ptr, "mat_nr", 0, NULL, ICON_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpencil_modifier_panel_end(layout, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
|
||||||
|
{
|
||||||
|
gpencil_modifier_masking_panel_draw(panel, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void panelRegister(ARegionType *region_type)
|
||||||
|
{
|
||||||
|
PanelType *panel_type = gpencil_modifier_panel_register(
|
||||||
|
region_type, eGpencilModifierType_Envelope, panel_draw);
|
||||||
|
PanelType *mask_panel_type = gpencil_modifier_subpanel_register(
|
||||||
|
region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
GpencilModifierTypeInfo modifierType_Gpencil_Envelope = {
|
||||||
|
/* name */ "Envelope",
|
||||||
|
/* structName */ "EnvelopeGpencilModifierData",
|
||||||
|
/* structSize */ sizeof(EnvelopeGpencilModifierData),
|
||||||
|
/* type */ eGpencilModifierTypeType_Gpencil,
|
||||||
|
/* flags */ eGpencilModifierTypeFlag_SupportsEditmode,
|
||||||
|
|
||||||
|
/* copyData */ copyData,
|
||||||
|
|
||||||
|
/* deformStroke */ deformStroke,
|
||||||
|
/* generateStrokes */ generateStrokes,
|
||||||
|
/* bakeModifier */ bakeModifier,
|
||||||
|
/* remapTime */ NULL,
|
||||||
|
|
||||||
|
/* initData */ initData,
|
||||||
|
/* freeData */ NULL,
|
||||||
|
/* isDisabled */ NULL,
|
||||||
|
/* updateDepsgraph */ NULL,
|
||||||
|
/* dependsOnTime */ NULL,
|
||||||
|
/* foreachIDLink */ foreachIDLink,
|
||||||
|
/* foreachTexLink */ NULL,
|
||||||
|
/* panelRegister */ panelRegister,
|
||||||
|
};
|
||||||
@@ -366,5 +366,14 @@
|
|||||||
.smooth_step = 1, \
|
.smooth_step = 1, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define _DNA_DEFAULT_EnvelopeGpencilModifierData \
|
||||||
|
{ \
|
||||||
|
.spread = 10, \
|
||||||
|
.mode = GP_ENVELOPE_SEGMENTS, \
|
||||||
|
.mat_nr = -1, \
|
||||||
|
.thickness = 1.0f, \
|
||||||
|
.strength = 1.0f, \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* clang-format off */
|
/* clang-format off */
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ typedef enum GpencilModifierType {
|
|||||||
eGpencilModifierType_Dash = 22,
|
eGpencilModifierType_Dash = 22,
|
||||||
eGpencilModifierType_WeightAngle = 23,
|
eGpencilModifierType_WeightAngle = 23,
|
||||||
eGpencilModifierType_Shrinkwrap = 24,
|
eGpencilModifierType_Shrinkwrap = 24,
|
||||||
|
eGpencilModifierType_Envelope = 25,
|
||||||
/* Keep last. */
|
/* Keep last. */
|
||||||
NUM_GREASEPENCIL_MODIFIER_TYPES,
|
NUM_GREASEPENCIL_MODIFIER_TYPES,
|
||||||
} GpencilModifierType;
|
} GpencilModifierType;
|
||||||
@@ -1127,6 +1128,46 @@ typedef enum eShrinkwrapGpencil_Flag {
|
|||||||
GP_SHRINKWRAP_INVERT_VGROUP = (1 << 6),
|
GP_SHRINKWRAP_INVERT_VGROUP = (1 << 6),
|
||||||
} eShrinkwrapGpencil_Flag;
|
} eShrinkwrapGpencil_Flag;
|
||||||
|
|
||||||
|
typedef struct EnvelopeGpencilModifierData {
|
||||||
|
GpencilModifierData modifier;
|
||||||
|
/** Material for filtering. */
|
||||||
|
struct Material *material;
|
||||||
|
/** Layer name. */
|
||||||
|
char layername[64];
|
||||||
|
/** Optional vertexgroup name, MAX_VGROUP_NAME. */
|
||||||
|
char vgname[64];
|
||||||
|
/** Custom index for passes. */
|
||||||
|
int pass_index;
|
||||||
|
/** Several flags. */
|
||||||
|
int flag;
|
||||||
|
int mode;
|
||||||
|
/** Material for the new strokes. */
|
||||||
|
int mat_nr;
|
||||||
|
/** Thickness multiplier for the new strokes. */
|
||||||
|
float thickness;
|
||||||
|
/** Strength multiplier for the new strokes. */
|
||||||
|
float strength;
|
||||||
|
/** Custom index for passes. */
|
||||||
|
int layer_pass;
|
||||||
|
/* Length of the envelope effect. */
|
||||||
|
int spread;
|
||||||
|
} EnvelopeGpencilModifierData;
|
||||||
|
|
||||||
|
typedef enum eEnvelopeGpencil_Flag {
|
||||||
|
GP_ENVELOPE_INVERT_LAYER = (1 << 0),
|
||||||
|
GP_ENVELOPE_INVERT_PASS = (1 << 1),
|
||||||
|
GP_ENVELOPE_INVERT_VGROUP = (1 << 2),
|
||||||
|
GP_ENVELOPE_INVERT_LAYERPASS = (1 << 3),
|
||||||
|
GP_ENVELOPE_INVERT_MATERIAL = (1 << 4),
|
||||||
|
} eEnvelopeGpencil_Flag;
|
||||||
|
|
||||||
|
/* Texture->mode */
|
||||||
|
typedef enum eEnvelopeGpencil_Mode {
|
||||||
|
GP_ENVELOPE_DEFORM = 0,
|
||||||
|
GP_ENVELOPE_SEGMENTS = 1,
|
||||||
|
GP_ENVELOPE_FILLS = 2,
|
||||||
|
} eEnvelopeGpencil_Mode;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -316,6 +316,7 @@ SDNA_DEFAULT_DECL_STRUCT(LengthGpencilModifierData);
|
|||||||
SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierData);
|
SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierData);
|
||||||
SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierSegment);
|
SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierSegment);
|
||||||
SDNA_DEFAULT_DECL_STRUCT(ShrinkwrapGpencilModifierData);
|
SDNA_DEFAULT_DECL_STRUCT(ShrinkwrapGpencilModifierData);
|
||||||
|
SDNA_DEFAULT_DECL_STRUCT(EnvelopeGpencilModifierData);
|
||||||
|
|
||||||
#undef SDNA_DEFAULT_DECL_STRUCT
|
#undef SDNA_DEFAULT_DECL_STRUCT
|
||||||
|
|
||||||
@@ -555,6 +556,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
|
|||||||
SDNA_DEFAULT_DECL(DashGpencilModifierData),
|
SDNA_DEFAULT_DECL(DashGpencilModifierData),
|
||||||
SDNA_DEFAULT_DECL(DashGpencilModifierSegment),
|
SDNA_DEFAULT_DECL(DashGpencilModifierSegment),
|
||||||
SDNA_DEFAULT_DECL(ShrinkwrapGpencilModifierData),
|
SDNA_DEFAULT_DECL(ShrinkwrapGpencilModifierData),
|
||||||
|
SDNA_DEFAULT_DECL(EnvelopeGpencilModifierData),
|
||||||
};
|
};
|
||||||
#undef SDNA_DEFAULT_DECL
|
#undef SDNA_DEFAULT_DECL
|
||||||
#undef SDNA_DEFAULT_DECL_EX
|
#undef SDNA_DEFAULT_DECL_EX
|
||||||
|
|||||||
@@ -79,6 +79,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
|
|||||||
ICON_MOD_DASH,
|
ICON_MOD_DASH,
|
||||||
"Dot Dash",
|
"Dot Dash",
|
||||||
"Generate dot-dash styled strokes"},
|
"Generate dot-dash styled strokes"},
|
||||||
|
{eGpencilModifierType_Envelope,
|
||||||
|
"GP_ENVELOPE",
|
||||||
|
ICON_MOD_SKIN,
|
||||||
|
"Envelope",
|
||||||
|
"Create an envelope shape"},
|
||||||
{eGpencilModifierType_Length,
|
{eGpencilModifierType_Length,
|
||||||
"GP_LENGTH",
|
"GP_LENGTH",
|
||||||
ICON_MOD_LENGTH,
|
ICON_MOD_LENGTH,
|
||||||
@@ -208,6 +213,25 @@ static const EnumPropertyItem gpencil_length_mode_items[] = {
|
|||||||
{GP_LENGTH_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Length in geometry space"},
|
{GP_LENGTH_ABSOLUTE, "ABSOLUTE", 0, "Absolute", "Length in geometry space"},
|
||||||
{0, NULL, 0, NULL, NULL},
|
{0, NULL, 0, NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const EnumPropertyItem gpencil_envelope_mode_items[] = {
|
||||||
|
{GP_ENVELOPE_DEFORM,
|
||||||
|
"DEFORM",
|
||||||
|
0,
|
||||||
|
"Deform",
|
||||||
|
"Deform the stroke to best match the envelope shape"},
|
||||||
|
{GP_ENVELOPE_SEGMENTS,
|
||||||
|
"SEGMENTS",
|
||||||
|
0,
|
||||||
|
"Segments",
|
||||||
|
"Add segments to create the envelope. Keep the original stroke"},
|
||||||
|
{GP_ENVELOPE_FILLS,
|
||||||
|
"FILLS",
|
||||||
|
0,
|
||||||
|
"Fills",
|
||||||
|
"Add fill segments to create the envelope. Don't keep the original stroke"},
|
||||||
|
{0, NULL, 0, NULL, NULL},
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef RNA_RUNTIME
|
#ifdef RNA_RUNTIME
|
||||||
@@ -279,6 +303,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr)
|
|||||||
return &RNA_LineartGpencilModifier;
|
return &RNA_LineartGpencilModifier;
|
||||||
case eGpencilModifierType_Dash:
|
case eGpencilModifierType_Dash:
|
||||||
return &RNA_DashGpencilModifierData;
|
return &RNA_DashGpencilModifierData;
|
||||||
|
case eGpencilModifierType_Envelope:
|
||||||
|
return &RNA_EnvelopeGpencilModifier;
|
||||||
/* Default */
|
/* Default */
|
||||||
case eGpencilModifierType_None:
|
case eGpencilModifierType_None:
|
||||||
case NUM_GREASEPENCIL_MODIFIER_TYPES:
|
case NUM_GREASEPENCIL_MODIFIER_TYPES:
|
||||||
@@ -355,6 +381,7 @@ RNA_GP_MOD_VGROUP_NAME_SET(WeightAngle, target_vgname);
|
|||||||
RNA_GP_MOD_VGROUP_NAME_SET(WeightAngle, vgname);
|
RNA_GP_MOD_VGROUP_NAME_SET(WeightAngle, vgname);
|
||||||
RNA_GP_MOD_VGROUP_NAME_SET(Lineart, vgname);
|
RNA_GP_MOD_VGROUP_NAME_SET(Lineart, vgname);
|
||||||
RNA_GP_MOD_VGROUP_NAME_SET(Shrinkwrap, vgname);
|
RNA_GP_MOD_VGROUP_NAME_SET(Shrinkwrap, vgname);
|
||||||
|
RNA_GP_MOD_VGROUP_NAME_SET(Envelope, vgname);
|
||||||
|
|
||||||
# undef RNA_GP_MOD_VGROUP_NAME_SET
|
# undef RNA_GP_MOD_VGROUP_NAME_SET
|
||||||
|
|
||||||
@@ -775,6 +802,16 @@ static void rna_ShrinkwrapGpencilModifier_face_cull_set(struct PointerRNA *ptr,
|
|||||||
swm->shrink_opts = (swm->shrink_opts & ~MOD_SHRINKWRAP_CULL_TARGET_MASK) | value;
|
swm->shrink_opts = (swm->shrink_opts & ~MOD_SHRINKWRAP_CULL_TARGET_MASK) | value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rna_EnvelopeGpencilModifier_material_set(PointerRNA *ptr,
|
||||||
|
PointerRNA value,
|
||||||
|
struct ReportList *reports)
|
||||||
|
{
|
||||||
|
EnvelopeGpencilModifierData *emd = (EnvelopeGpencilModifierData *)ptr->data;
|
||||||
|
Material **ma_target = &emd->material;
|
||||||
|
|
||||||
|
rna_GpencilModifier_material_set(ptr, value, ma_target, reports);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static void rna_def_modifier_gpencilnoise(BlenderRNA *brna)
|
static void rna_def_modifier_gpencilnoise(BlenderRNA *brna)
|
||||||
@@ -3968,6 +4005,112 @@ static void rna_def_modifier_gpencilshrinkwrap(BlenderRNA *brna)
|
|||||||
RNA_define_lib_overridable(false);
|
RNA_define_lib_overridable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rna_def_modifier_gpencilenvelope(BlenderRNA *brna)
|
||||||
|
{
|
||||||
|
StructRNA *srna;
|
||||||
|
PropertyRNA *prop;
|
||||||
|
|
||||||
|
srna = RNA_def_struct(brna, "EnvelopeGpencilModifier", "GpencilModifier");
|
||||||
|
RNA_def_struct_ui_text(srna, "Envelope Modifier", "Envelope stroke effect modifier");
|
||||||
|
RNA_def_struct_sdna(srna, "EnvelopeGpencilModifierData");
|
||||||
|
RNA_def_struct_ui_icon(srna, ICON_MOD_SKIN);
|
||||||
|
|
||||||
|
RNA_define_lib_overridable(true);
|
||||||
|
|
||||||
|
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");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE);
|
||||||
|
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||||
|
RNA_def_property_pointer_funcs(prop,
|
||||||
|
NULL,
|
||||||
|
"rna_EnvelopeGpencilModifier_material_set",
|
||||||
|
NULL,
|
||||||
|
"rna_GpencilModifier_material_poll");
|
||||||
|
RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE);
|
||||||
|
RNA_def_property_string_sdna(prop, NULL, "vgname");
|
||||||
|
RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modulating the deform");
|
||||||
|
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_EnvelopeGpencilModifier_vgname_set");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
|
||||||
|
RNA_def_property_int_sdna(prop, NULL, "pass_index");
|
||||||
|
RNA_def_property_range(prop, 0, 100);
|
||||||
|
RNA_def_property_ui_text(prop, "Pass", "Pass index");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "spread", PROP_INT, PROP_NONE);
|
||||||
|
RNA_def_property_int_sdna(prop, NULL, "spread");
|
||||||
|
RNA_def_property_range(prop, 1, INT_MAX);
|
||||||
|
RNA_def_property_ui_text(
|
||||||
|
prop, "Spread Length", "The number of points to skip to create straight segments");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
|
||||||
|
RNA_def_property_enum_sdna(prop, NULL, "mode");
|
||||||
|
RNA_def_property_enum_items(prop, gpencil_envelope_mode_items);
|
||||||
|
RNA_def_property_ui_text(prop, "Mode", "Algorithm to use for generating the envelope");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "mat_nr", PROP_INT, PROP_NONE);
|
||||||
|
RNA_def_property_int_sdna(prop, NULL, "mat_nr");
|
||||||
|
RNA_def_property_range(prop, -1, INT16_MAX);
|
||||||
|
RNA_def_property_ui_text(prop, "Material Index", "The material to use for the new strokes");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "thickness", PROP_FLOAT, PROP_FACTOR);
|
||||||
|
RNA_def_property_float_sdna(prop, NULL, "thickness");
|
||||||
|
RNA_def_property_range(prop, 0, FLT_MAX);
|
||||||
|
RNA_def_property_ui_range(prop, 0, 1, 10, 3);
|
||||||
|
RNA_def_property_ui_text(prop, "Thickness", "Multiplier for the thickness of the new strokes");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_FACTOR);
|
||||||
|
RNA_def_property_float_sdna(prop, NULL, "strength");
|
||||||
|
RNA_def_property_range(prop, 0, FLT_MAX);
|
||||||
|
RNA_def_property_ui_range(prop, 0, 1, 10, 3);
|
||||||
|
RNA_def_property_ui_text(prop, "Strength", "Multiplier for the strength of the new strokes");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ENVELOPE_INVERT_LAYER);
|
||||||
|
RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ENVELOPE_INVERT_MATERIAL);
|
||||||
|
RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ENVELOPE_INVERT_PASS);
|
||||||
|
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "invert_vertex", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ENVELOPE_INVERT_VGROUP);
|
||||||
|
RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE);
|
||||||
|
RNA_def_property_int_sdna(prop, NULL, "layer_pass");
|
||||||
|
RNA_def_property_range(prop, 0, 100);
|
||||||
|
RNA_def_property_ui_text(prop, "Pass", "Layer pass index");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_ENVELOPE_INVERT_LAYERPASS);
|
||||||
|
RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter");
|
||||||
|
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||||
|
|
||||||
|
RNA_define_lib_overridable(false);
|
||||||
|
}
|
||||||
|
|
||||||
void RNA_def_greasepencil_modifier(BlenderRNA *brna)
|
void RNA_def_greasepencil_modifier(BlenderRNA *brna)
|
||||||
{
|
{
|
||||||
StructRNA *srna;
|
StructRNA *srna;
|
||||||
@@ -4058,6 +4201,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna)
|
|||||||
rna_def_modifier_gpencillength(brna);
|
rna_def_modifier_gpencillength(brna);
|
||||||
rna_def_modifier_gpencildash(brna);
|
rna_def_modifier_gpencildash(brna);
|
||||||
rna_def_modifier_gpencilshrinkwrap(brna);
|
rna_def_modifier_gpencilshrinkwrap(brna);
|
||||||
|
rna_def_modifier_gpencilenvelope(brna);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user