GPencil: Merge GSoC curve edit mode

Differential Revision: https://developer.blender.org/D8660

This patch is the result of the GSoC 2020 "Editing Grease Pencil Strokes
Using Curves" project. It adds a submode to greasepencil edit mode that
allows for the transformation of greasepencil strokes using bezier
curves. More information about the project can be found
here: https://wiki.blender.org/wiki/User:Filedescriptor/GSoC_2020.
This commit is contained in:
Falk David 2020-11-13 21:43:00 +01:00
parent 9d28353b52
commit 0be88c7d15
Notes: blender-bot 2023-02-14 06:37:09 +01:00
Referenced by issue #84260, NURBS Surface Edit Mode: Spline Cage of Unselected Surface Is Not Fully Rendered
Referenced by issue #80192, GPencil: New Bezier edition (GSoC)
63 changed files with 5355 additions and 1081 deletions

View File

@ -191,6 +191,7 @@ _km_hierarchy = [
]),
('Grease Pencil', 'EMPTY', 'WINDOW', [ # grease pencil stuff (per region)
('Grease Pencil Stroke Curve Edit Mode', 'EMPTY', 'WINDOW', []),
('Grease Pencil Stroke Edit Mode', 'EMPTY', 'WINDOW', []),
('Grease Pencil Stroke Paint (Draw brush)', 'EMPTY', 'WINDOW', []),
('Grease Pencil Stroke Paint (Fill)', 'EMPTY', 'WINDOW', []),

View File

@ -3226,6 +3226,9 @@ def km_grease_pencil_stroke_edit_mode(params):
{"properties": [("mode", 'GPENCIL_OPACITY')]}),
# Proportional editing.
*_template_items_proportional_editing(connected=True),
# Curve edit mode toggle.
("wm.context_toggle", {"type": 'U', "value": 'PRESS'},
{"properties": [("data_path", 'gpencil_data.use_curve_edit')]}),
# Add menu
("object.gpencil_add", {"type": 'A', "value": 'PRESS', "shift": True}, None),
# Vertex group menu
@ -3253,6 +3256,20 @@ def km_grease_pencil_stroke_edit_mode(params):
return keymap
def km_grease_pencil_stroke_curve_edit_mode(params):
items = []
keymap = (
"Grease Pencil Stroke Curve Edit Mode",
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": items},
)
items.extend([
# Set handle type
("gpencil.stroke_editcurve_set_handle_type", {"type": 'V', "value": 'PRESS'}, None),
])
return keymap
def km_grease_pencil_stroke_paint_mode(params):
items = []
@ -6890,6 +6907,7 @@ def generate_keymaps(params=None):
# Modes.
km_grease_pencil(params),
km_grease_pencil_stroke_curve_edit_mode(params),
km_grease_pencil_stroke_edit_mode(params),
km_grease_pencil_stroke_paint_mode(params),
km_grease_pencil_stroke_paint_draw_brush(params),

View File

@ -369,6 +369,8 @@ class DATA_PT_gpencil_strokes(DataButtonsPanel, Panel):
sub.active = gpd.stroke_thickness_space == 'WORLDSPACE'
sub.prop(gpd, "pixel_factor", text="Thickness Scale")
col.prop(gpd, "edit_curve_resolution")
class DATA_PT_gpencil_display(DataButtonsPanel, Panel):
bl_label = "Viewport Display"

View File

@ -661,7 +661,23 @@ class VIEW3D_HT_header(Header):
# Select mode for Editing
if gpd.use_stroke_edit_mode:
row = layout.row(align=True)
row.prop(tool_settings, "gpencil_selectmode_edit", text="", expand=True)
row.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='POINT')
row.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='STROKE')
subrow = row.row(align=True)
subrow.enabled = not gpd.use_curve_edit
subrow.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='SEGMENT')
# Curve edit submode
row = layout.row(align=True)
row.prop(gpd, "use_curve_edit", text="",
icon='IPO_BEZIER')
sub = row.row(align=True)
sub.active = gpd.use_curve_edit
sub.popover(
panel="VIEW3D_PT_gpencil_curve_edit",
text="Curve Editing",
)
# Select mode for Sculpt
if gpd.is_stroke_sculpt_mode:
@ -687,7 +703,7 @@ class VIEW3D_HT_header(Header):
row.prop(gpd, "use_multiedit", text="", icon='GP_MULTIFRAME_EDITING')
sub = row.row(align=True)
sub.active = gpd.use_multiedit
sub.enabled = gpd.use_multiedit
sub.popover(
panel="VIEW3D_PT_gpencil_multi_frame",
text="Multiframe",
@ -6802,6 +6818,12 @@ class VIEW3D_PT_overlay_gpencil_options(Panel):
layout.prop(overlay, "vertex_opacity", text="Vertex Opacity", slider=True)
# Handles for Curve Edit
if context.object.mode == 'EDIT_GPENCIL':
gpd = context.object.data
if gpd.use_curve_edit:
layout.prop(overlay, "display_handle", text="Handles")
if context.object.mode in {'PAINT_GPENCIL', 'VERTEX_GPENCIL'}:
layout.label(text="Vertex Paint")
row = layout.row()
@ -6965,6 +6987,24 @@ class VIEW3D_PT_gpencil_multi_frame(Panel):
layout.template_curve_mapping(settings, "multiframe_falloff_curve", brush=True)
# Grease Pencil Object - Curve Editing tools
class VIEW3D_PT_gpencil_curve_edit(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
bl_label = "Curve Editing"
def draw(self, context):
gpd = context.gpencil_data
settings = context.tool_settings.gpencil_sculpt
layout = self.layout
col = layout.column(align=True)
col.prop(gpd, "edit_curve_resolution")
col.prop(gpd, "curve_edit_threshold")
col.prop(gpd, "curve_edit_corner_angle")
col.prop(gpd, "use_adaptive_curve_resolution")
class VIEW3D_MT_gpencil_edit_context_menu(Menu):
bl_label = ""
@ -7606,6 +7646,7 @@ classes = (
VIEW3D_PT_grease_pencil,
VIEW3D_PT_annotation_onion,
VIEW3D_PT_gpencil_multi_frame,
VIEW3D_PT_gpencil_curve_edit,
VIEW3D_PT_quad_view,
VIEW3D_PT_view3d_stereo,
VIEW3D_PT_shading,

View File

@ -48,6 +48,7 @@ struct bGPDlayer;
struct bGPDlayer_Mask;
struct bGPDspoint;
struct bGPDstroke;
struct bGPDcurve;
struct bGPdata;
#define GPENCIL_SIMPLIFY(scene) ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE))
@ -89,6 +90,7 @@ struct bGPdata;
void BKE_gpencil_free_point_weights(struct MDeformVert *dvert);
void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps);
void BKE_gpencil_free_stroke_editcurve(struct bGPDstroke *gps);
void BKE_gpencil_free_stroke(struct bGPDstroke *gps);
bool BKE_gpencil_free_strokes(struct bGPDframe *gpf);
void BKE_gpencil_free_frames(struct bGPDlayer *gpl);
@ -102,6 +104,7 @@ void BKE_gpencil_batch_cache_dirty_tag(struct bGPdata *gpd);
void BKE_gpencil_batch_cache_free(struct bGPdata *gpd);
void BKE_gpencil_stroke_sync_selection(struct bGPDstroke *gps);
void BKE_gpencil_curve_sync_selection(struct bGPDstroke *gps);
struct bGPDframe *BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe);
@ -111,7 +114,10 @@ struct bGPdata *BKE_gpencil_data_addnew(struct Main *bmain, const char name[]);
struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src);
struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src);
void BKE_gpencil_frame_copy_strokes(struct bGPDframe *gpf_src, struct bGPDframe *gpf_dst);
struct bGPDstroke *BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, const bool dup_points);
struct bGPDcurve *BKE_gpencil_stroke_curve_duplicate(struct bGPDcurve *gpc_src);
struct bGPDstroke *BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src,
const bool dup_points,
const bool dup_curve);
struct bGPdata *BKE_gpencil_data_duplicate(struct Main *bmain,
const struct bGPdata *gpd,
@ -160,6 +166,8 @@ struct bGPDstroke *BKE_gpencil_stroke_add_existing_style(struct bGPDframe *gpf,
int totpoints,
short thickness);
struct bGPDcurve *BKE_gpencil_stroke_editcurve_new(const int tot_curve_points);
/* Stroke and Fill - Alpha Visibility Threshold */
#define GPENCIL_ALPHA_OPACITY_THRESH 0.001f
#define GPENCIL_STRENGTH_MIN 0.003f
@ -247,6 +255,7 @@ float BKE_gpencil_multiframe_falloff_calc(
void BKE_gpencil_palette_ensure(struct Main *bmain, struct Scene *scene);
bool BKE_gpencil_from_image(struct SpaceImage *sima,
struct bGPdata *gpd,
struct bGPDframe *gpf,
const float size,
const bool mask);

View File

@ -30,6 +30,10 @@ extern "C" {
struct Main;
struct Object;
struct Scene;
struct bGPdata;
struct bGPDlayer;
struct bGPDstroke;
struct bGPDcurve;
void BKE_gpencil_convert_curve(struct Main *bmain,
struct Scene *scene,
@ -39,6 +43,23 @@ void BKE_gpencil_convert_curve(struct Main *bmain,
const float scale_thickness,
const float sample);
struct bGPDcurve *BKE_gpencil_stroke_editcurve_generate(struct bGPDstroke *gps,
const float error_threshold,
const float corner_angle,
const float stroke_radius);
void BKE_gpencil_stroke_editcurve_update(struct bGPdata *gpd,
struct bGPDlayer *gpl,
struct bGPDstroke *gps);
void BKE_gpencil_editcurve_stroke_sync_selection(struct bGPDstroke *gps, struct bGPDcurve *gpc);
void BKE_gpencil_stroke_editcurve_sync_selection(struct bGPDstroke *gps, struct bGPDcurve *gpc);
void BKE_gpencil_strokes_selected_update_editcurve(struct bGPdata *gpd);
void BKE_gpencil_strokes_selected_sync_selection_editcurve(struct bGPdata *gpd);
void BKE_gpencil_stroke_update_geometry_from_editcurve(struct bGPDstroke *gps,
const uint resolution,
const bool is_adaptive);
void BKE_gpencil_editcurve_recalculate_handles(struct bGPDstroke *gps);
void BKE_gpencil_editcurve_subdivide(struct bGPDstroke *gps, const int cuts);
#ifdef __cplusplus
}
#endif

View File

@ -51,11 +51,17 @@ void BKE_gpencil_stroke_boundingbox_calc(struct bGPDstroke *gps);
/* stroke geometry utilities */
void BKE_gpencil_stroke_normal(const struct bGPDstroke *gps, float r_normal[3]);
void BKE_gpencil_stroke_simplify_adaptive(struct bGPDstroke *gps, float epsilon);
void BKE_gpencil_stroke_simplify_fixed(struct bGPDstroke *gps);
void BKE_gpencil_stroke_subdivide(struct bGPDstroke *gps, int level, int type);
bool BKE_gpencil_stroke_trim(struct bGPDstroke *gps);
void BKE_gpencil_stroke_merge_distance(struct bGPDframe *gpf,
void BKE_gpencil_stroke_simplify_adaptive(struct bGPdata *gpd,
struct bGPDstroke *gps,
float epsilon);
void BKE_gpencil_stroke_simplify_fixed(struct bGPdata *gpd, struct bGPDstroke *gps);
void BKE_gpencil_stroke_subdivide(struct bGPdata *gpd,
struct bGPDstroke *gps,
int level,
int type);
bool BKE_gpencil_stroke_trim(struct bGPdata *gpd, struct bGPDstroke *gps);
void BKE_gpencil_stroke_merge_distance(struct bGPdata *gpd,
struct bGPDframe *gpf,
struct bGPDstroke *gps,
const float threshold,
const bool use_unselected);
@ -72,7 +78,7 @@ void BKE_gpencil_stroke_2d_flat_ref(const struct bGPDspoint *ref_points,
const float scale,
int *r_direction);
void BKE_gpencil_stroke_fill_triangulate(struct bGPDstroke *gps);
void BKE_gpencil_stroke_geometry_update(struct bGPDstroke *gps);
void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps);
void BKE_gpencil_stroke_uv_update(struct bGPDstroke *gps);
void BKE_gpencil_transform(struct bGPdata *gpd, const float mat[4][4]);
@ -91,19 +97,26 @@ void BKE_gpencil_point_coords_apply_with_mat4(struct bGPdata *gpd,
const GPencilPointCoordinates *elem_data,
const float mat[4][4]);
bool BKE_gpencil_stroke_sample(struct bGPDstroke *gps, const float dist, const bool select);
bool BKE_gpencil_stroke_sample(struct bGPdata *gpd,
struct bGPDstroke *gps,
const float dist,
const bool select);
bool BKE_gpencil_stroke_smooth(struct bGPDstroke *gps, int i, float inf);
bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence);
bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence);
bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence);
bool BKE_gpencil_stroke_close(struct bGPDstroke *gps);
void BKE_gpencil_dissolve_points(struct bGPDframe *gpf, struct bGPDstroke *gps, const short tag);
void BKE_gpencil_dissolve_points(struct bGPdata *gpd,
struct bGPDframe *gpf,
struct bGPDstroke *gps,
const short tag);
bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float tip_length);
bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps,
const int index_from,
const int index_to);
bool BKE_gpencil_stroke_split(struct bGPDframe *gpf,
bool BKE_gpencil_stroke_split(struct bGPdata *gpd,
struct bGPDframe *gpf,
struct bGPDstroke *gps,
const int before_index,
struct bGPDstroke **remaining_gps);

View File

@ -155,6 +155,11 @@ static void greasepencil_blend_write(BlendWriter *writer, ID *id, const void *id
BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points);
BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles);
BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert);
if (gps->editcurve != NULL) {
bGPDcurve *gpc = gps->editcurve;
BLO_write_struct(writer, bGPDcurve, gpc);
BLO_write_struct_array(writer, bGPDcurve_point, gpc->tot_curve_points, gpc->curve_points);
}
}
}
}
@ -222,6 +227,13 @@ void BKE_gpencil_blend_read_data(BlendDataReader *reader, bGPdata *gpd)
/* Relink geometry*/
BLO_read_data_address(reader, &gps->triangles);
/* relink stroke edit curve. */
BLO_read_data_address(reader, &gps->editcurve);
if (gps->editcurve != NULL) {
/* relink curve point array */
BLO_read_data_address(reader, &gps->editcurve->curve_points);
}
/* relink weight data */
if (gps->dvert) {
BLO_read_data_address(reader, &gps->dvert);
@ -341,6 +353,20 @@ void BKE_gpencil_free_stroke_weights(bGPDstroke *gps)
}
}
void BKE_gpencil_free_stroke_editcurve(bGPDstroke *gps)
{
if (gps == NULL) {
return;
}
bGPDcurve *editcurve = gps->editcurve;
if (editcurve == NULL) {
return;
}
MEM_freeN(editcurve->curve_points);
MEM_freeN(editcurve);
gps->editcurve = NULL;
}
/* free stroke, doesn't unlink from any listbase */
void BKE_gpencil_free_stroke(bGPDstroke *gps)
{
@ -358,6 +384,9 @@ void BKE_gpencil_free_stroke(bGPDstroke *gps)
if (gps->triangles) {
MEM_freeN(gps->triangles);
}
if (gps->editcurve != NULL) {
BKE_gpencil_free_stroke_editcurve(gps);
}
MEM_freeN(gps);
}
@ -688,6 +717,13 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
gpd->curve_edit_resolution = GP_DEFAULT_CURVE_RESOLUTION;
gpd->curve_edit_threshold = GP_DEFAULT_CURVE_ERROR;
gpd->curve_edit_corner_angle = GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE;
/* use adaptive curve resolution by default */
gpd->flag |= GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
gpd->zdepth_offset = 0.150f;
/* grid settings */
@ -776,6 +812,8 @@ bGPDstroke *BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness)
gps->mat_nr = mat_idx;
gps->editcurve = NULL;
return gps;
}
@ -827,6 +865,16 @@ bGPDstroke *BKE_gpencil_stroke_add_existing_style(
return gps;
}
bGPDcurve *BKE_gpencil_stroke_editcurve_new(const int tot_curve_points)
{
bGPDcurve *new_gp_curve = (bGPDcurve *)MEM_callocN(sizeof(bGPDcurve), __func__);
new_gp_curve->tot_curve_points = tot_curve_points;
new_gp_curve->curve_points = (bGPDcurve_point *)MEM_callocN(
sizeof(bGPDcurve_point) * tot_curve_points, __func__);
return new_gp_curve;
}
/* ************************************************** */
/* Data Duplication */
@ -845,13 +893,28 @@ void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_d
BKE_defvert_array_copy(gps_dst->dvert, gps_src->dvert, gps_src->totpoints);
}
/* Make a copy of a given gpencil stroke editcurve */
bGPDcurve *BKE_gpencil_stroke_curve_duplicate(bGPDcurve *gpc_src)
{
bGPDcurve *gpc_dst = MEM_dupallocN(gpc_src);
if (gpc_src->curve_points != NULL) {
gpc_dst->curve_points = MEM_dupallocN(gpc_src->curve_points);
}
return gpc_dst;
}
/**
* Make a copy of a given grease-pencil stroke.
* \param gps_src: Source grease pencil strokes.
* \param dup_points: Duplicate points data.
* \param dup_curve: Duplicate curve data.
* \return Pointer to new stroke.
*/
bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src, const bool dup_points)
bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src,
const bool dup_points,
const bool dup_curve)
{
bGPDstroke *gps_dst = NULL;
@ -871,6 +934,10 @@ bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src, const bool dup_poi
}
}
if (dup_curve && gps_src->editcurve != NULL) {
gps_dst->editcurve = BKE_gpencil_stroke_curve_duplicate(gps_src->editcurve);
}
/* return new stroke */
return gps_dst;
}
@ -898,7 +965,7 @@ bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src)
BLI_listbase_clear(&gpf_dst->strokes);
LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
/* make copy of source stroke */
gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
BLI_addtail(&gpf_dst->strokes, gps_dst);
}
@ -923,7 +990,7 @@ void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, struct bGPDframe *gpf_ds
BLI_listbase_clear(&gpf_dst->strokes);
LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
/* make copy of source stroke */
gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
BLI_addtail(&gpf_dst->strokes, gps_dst);
}
}
@ -1037,6 +1104,39 @@ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps)
}
}
void BKE_gpencil_curve_sync_selection(bGPDstroke *gps)
{
bGPDcurve *gpc = gps->editcurve;
if (gpc == NULL) {
return;
}
gps->flag &= ~GP_STROKE_SELECT;
gpc->flag &= ~GP_CURVE_SELECT;
bool is_selected = false;
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
if (BEZT_ISSEL_ANY(bezt)) {
gpc_pt->flag |= GP_SPOINT_SELECT;
}
else {
gpc_pt->flag &= ~GP_SPOINT_SELECT;
}
if (gpc_pt->flag & GP_SPOINT_SELECT) {
is_selected = true;
}
}
if (is_selected) {
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
}
}
/* ************************************************** */
/* GP Frame API */
@ -2304,12 +2404,14 @@ void BKE_gpencil_palette_ensure(Main *bmain, Scene *scene)
/**
* Create grease pencil strokes from image
* \param sima: Image
* \param gpd: Grease pencil data-block
* \param gpf: Grease pencil frame
* \param size: Size
* \param mask: Mask
* \return True if done
*/
bool BKE_gpencil_from_image(SpaceImage *sima, bGPDframe *gpf, const float size, const bool mask)
bool BKE_gpencil_from_image(
SpaceImage *sima, bGPdata *gpd, bGPDframe *gpf, const float size, const bool mask)
{
Image *image = sima->image;
bool done = false;
@ -2357,7 +2459,7 @@ bool BKE_gpencil_from_image(SpaceImage *sima, bGPDframe *gpf, const float size,
pt->flag |= GP_SPOINT_SELECT;
}
}
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}

View File

@ -37,8 +37,10 @@
#include "BLT_translation.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_collection.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_curve.h"
@ -47,8 +49,16 @@
#include "BKE_material.h"
#include "BKE_object.h"
#include "curve_fit_nd.h"
#include "DEG_depsgraph_query.h"
#define COORD_FITTING_INFLUENCE 20.0f
/* -------------------------------------------------------------------- */
/** \name Convert to curve object
* \{ */
/* Helper: Check materials with same color. */
static int gpencil_check_same_material_color(Object *ob_gp,
const float color_stroke[4],
@ -295,6 +305,7 @@ static void gpencil_convert_spline(Main *bmain,
bGPDframe *gpf,
Nurb *nu)
{
bGPdata *gpd = (bGPdata *)ob_gp->data;
bool cyclic = true;
/* Create Stroke. */
@ -445,11 +456,22 @@ static void gpencil_convert_spline(Main *bmain,
}
if (sample > 0.0f) {
BKE_gpencil_stroke_sample(gps, sample, false);
BKE_gpencil_stroke_sample(gpd, gps, sample, false);
}
/* Recalc fill geometry. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
static void gpencil_editstroke_deselect_all(bGPDcurve *gpc)
{
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
BEZT_DESEL_ALL(bezt);
}
gpc->flag &= ~GP_CURVE_SELECT;
}
/**
@ -536,3 +558,851 @@ void BKE_gpencil_convert_curve(Main *bmain,
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Editcurve kernel functions
* \{ */
static bGPDcurve *gpencil_stroke_editcurve_generate_edgecases(bGPDstroke *gps,
const float stroke_radius)
{
BLI_assert(gps->totpoints < 3);
if (gps->totpoints == 1) {
bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_new(1);
bGPDspoint *pt = &gps->points[0];
bGPDcurve_point *cpt = &editcurve->curve_points[0];
BezTriple *bezt = &cpt->bezt;
/* Handles are twice as long as the radius of the point. */
float offset = (pt->pressure * stroke_radius) * 2.0f;
float tmp_vec[3];
for (int j = 0; j < 3; j++) {
copy_v3_v3(tmp_vec, &pt->x);
/* Move handles along the x-axis away from the control point */
tmp_vec[0] += (float)(j - 1) * offset;
copy_v3_v3(bezt->vec[j], tmp_vec);
}
cpt->pressure = pt->pressure;
cpt->strength = pt->strength;
copy_v4_v4(cpt->vert_color, pt->vert_color);
/* default handle type */
bezt->h1 = HD_FREE;
bezt->h2 = HD_FREE;
cpt->point_index = 0;
return editcurve;
}
if (gps->totpoints == 2) {
bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_new(2);
bGPDspoint *first_pt = &gps->points[0];
bGPDspoint *last_pt = &gps->points[1];
float length = len_v3v3(&first_pt->x, &last_pt->x);
float offset = length / 3;
float dir[3];
sub_v3_v3v3(dir, &last_pt->x, &first_pt->x);
for (int i = 0; i < 2; i++) {
bGPDspoint *pt = &gps->points[i];
bGPDcurve_point *cpt = &editcurve->curve_points[i];
BezTriple *bezt = &cpt->bezt;
float tmp_vec[3];
for (int j = 0; j < 3; j++) {
copy_v3_v3(tmp_vec, dir);
normalize_v3_length(tmp_vec, (float)(j - 1) * offset);
add_v3_v3v3(bezt->vec[j], &pt->x, tmp_vec);
}
cpt->pressure = pt->pressure;
cpt->strength = pt->strength;
copy_v4_v4(cpt->vert_color, pt->vert_color);
/* default handle type */
bezt->h1 = HD_VECT;
bezt->h2 = HD_VECT;
cpt->point_index = 0;
}
return editcurve;
}
return NULL;
}
/**
* Creates a bGPDcurve by doing a cubic curve fitting on the grease pencil stroke points.
*/
bGPDcurve *BKE_gpencil_stroke_editcurve_generate(bGPDstroke *gps,
const float error_threshold,
const float corner_angle,
const float stroke_radius)
{
if (gps->totpoints < 3) {
return gpencil_stroke_editcurve_generate_edgecases(gps, stroke_radius);
}
#define POINT_DIM 9
float *points = MEM_callocN(sizeof(float) * gps->totpoints * POINT_DIM, __func__);
float diag_length = len_v3v3(gps->boundbox_min, gps->boundbox_max);
float tmp_vec[3];
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
int row = i * POINT_DIM;
/* normalize coordinate to 0..1 */
sub_v3_v3v3(tmp_vec, &pt->x, gps->boundbox_min);
mul_v3_v3fl(&points[row], tmp_vec, COORD_FITTING_INFLUENCE / diag_length);
points[row + 3] = pt->pressure / diag_length;
/* strength and color are already normalized */
points[row + 4] = pt->strength / diag_length;
mul_v4_v4fl(&points[row + 5], pt->vert_color, 1.0f / diag_length);
}
uint calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
if (gps->totpoints > 2 && gps->flag & GP_STROKE_CYCLIC) {
calc_flag |= CURVE_FIT_CALC_CYCLIC;
}
float *r_cubic_array = NULL;
unsigned int r_cubic_array_len = 0;
unsigned int *r_cubic_orig_index = NULL;
unsigned int *r_corners_index_array = NULL;
unsigned int r_corners_index_len = 0;
int r = curve_fit_cubic_to_points_refit_fl(points,
gps->totpoints,
POINT_DIM,
error_threshold,
calc_flag,
NULL,
0,
corner_angle,
&r_cubic_array,
&r_cubic_array_len,
&r_cubic_orig_index,
&r_corners_index_array,
&r_corners_index_len);
if (r != 0 || r_cubic_array_len < 1) {
return NULL;
}
uint curve_point_size = 3 * POINT_DIM;
bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_new(r_cubic_array_len);
for (int i = 0; i < r_cubic_array_len; i++) {
bGPDcurve_point *cpt = &editcurve->curve_points[i];
BezTriple *bezt = &cpt->bezt;
float *curve_point = &r_cubic_array[i * curve_point_size];
for (int j = 0; j < 3; j++) {
float *bez = &curve_point[j * POINT_DIM];
madd_v3_v3v3fl(bezt->vec[j], gps->boundbox_min, bez, diag_length / COORD_FITTING_INFLUENCE);
}
float *ctrl_point = &curve_point[1 * POINT_DIM];
cpt->pressure = ctrl_point[3] * diag_length;
cpt->strength = ctrl_point[4] * diag_length;
mul_v4_v4fl(cpt->vert_color, &ctrl_point[5], diag_length);
/* default handle type */
bezt->h1 = HD_ALIGN;
bezt->h2 = HD_ALIGN;
cpt->point_index = r_cubic_orig_index[i];
}
if (r_corners_index_len > 0 && r_corners_index_array != NULL) {
int start = 0, end = r_corners_index_len;
if ((r_corners_index_len > 1) && (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0) {
start = 1;
end = r_corners_index_len - 1;
}
for (int i = start; i < end; i++) {
bGPDcurve_point *cpt = &editcurve->curve_points[r_corners_index_array[i]];
BezTriple *bezt = &cpt->bezt;
bezt->h1 = HD_FREE;
bezt->h2 = HD_FREE;
}
}
MEM_freeN(points);
if (r_cubic_array) {
free(r_cubic_array);
}
if (r_corners_index_array) {
free(r_corners_index_array);
}
if (r_cubic_orig_index) {
free(r_cubic_orig_index);
}
#undef POINT_DIM
return editcurve;
}
/**
* Updates the editcurve for a stroke. Frees the old curve if one exists and generates a new one.
*/
void BKE_gpencil_stroke_editcurve_update(bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps)
{
if (gps == NULL || gps->totpoints < 0) {
return;
}
if (gps->editcurve != NULL) {
BKE_gpencil_free_stroke_editcurve(gps);
}
float defaultpixsize = 1000.0f / gpd->pixfactor;
float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_generate(
gps, gpd->curve_edit_threshold, gpd->curve_edit_corner_angle, stroke_radius);
if (editcurve == NULL) {
return;
}
gps->editcurve = editcurve;
}
/**
* Sync the selection from stroke to editcurve
*/
void BKE_gpencil_editcurve_stroke_sync_selection(bGPDstroke *gps, bGPDcurve *gpc)
{
if (gps->flag & GP_STROKE_SELECT) {
gpc->flag |= GP_CURVE_SELECT;
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
bGPDspoint *pt = &gps->points[gpc_pt->point_index];
if (pt->flag & GP_SPOINT_SELECT) {
gpc_pt->flag |= GP_CURVE_POINT_SELECT;
BEZT_SEL_ALL(&gpc_pt->bezt);
}
else {
gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
BEZT_DESEL_ALL(&gpc_pt->bezt);
}
}
}
else {
gpc->flag &= ~GP_CURVE_SELECT;
gpencil_editstroke_deselect_all(gpc);
}
}
/**
* Sync the selection from editcurve to stroke
*/
void BKE_gpencil_stroke_editcurve_sync_selection(bGPDstroke *gps, bGPDcurve *gpc)
{
if (gpc->flag & GP_CURVE_SELECT) {
gps->flag |= GP_STROKE_SELECT;
for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
bGPDspoint *pt = &gps->points[gpc_pt->point_index];
bGPDcurve_point *gpc_pt_next = &gpc->curve_points[i + 1];
if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
pt->flag |= GP_SPOINT_SELECT;
if (gpc_pt_next->flag & GP_CURVE_POINT_SELECT) {
/* select all the points after */
for (int j = gpc_pt->point_index + 1; j < gpc_pt_next->point_index; j++) {
bGPDspoint *pt_next = &gps->points[j];
pt_next->flag |= GP_SPOINT_SELECT;
}
}
}
else {
pt->flag &= ~GP_SPOINT_SELECT;
/* deselect all points after */
for (int j = gpc_pt->point_index + 1; j < gpc_pt_next->point_index; j++) {
bGPDspoint *pt_next = &gps->points[j];
pt_next->flag &= ~GP_SPOINT_SELECT;
}
}
}
bGPDcurve_point *gpc_first = &gpc->curve_points[0];
bGPDcurve_point *gpc_last = &gpc->curve_points[gpc->tot_curve_points - 1];
bGPDspoint *last_pt = &gps->points[gpc_last->point_index];
if (gpc_last->flag & GP_CURVE_POINT_SELECT) {
last_pt->flag |= GP_SPOINT_SELECT;
}
else {
last_pt->flag &= ~GP_SPOINT_SELECT;
}
if (gps->flag & GP_STROKE_CYCLIC) {
if (gpc_first->flag & GP_CURVE_POINT_SELECT && gpc_last->flag & GP_CURVE_POINT_SELECT) {
for (int i = gpc_last->point_index + 1; i < gps->totpoints; i++) {
bGPDspoint *pt_next = &gps->points[i];
pt_next->flag |= GP_SPOINT_SELECT;
}
}
else {
for (int i = gpc_last->point_index + 1; i < gps->totpoints; i++) {
bGPDspoint *pt_next = &gps->points[i];
pt_next->flag &= ~GP_SPOINT_SELECT;
}
}
}
}
else {
gps->flag &= ~GP_STROKE_SELECT;
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
pt->flag &= ~GP_SPOINT_SELECT;
}
}
}
static void gpencil_interpolate_fl_from_to(
float from, float to, float *point_offset, int it, int stride)
{
/* smooth interpolation */
float *r = point_offset;
for (int i = 0; i <= it; i++) {
float fac = (float)i / (float)it;
fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; // smooth
*r = interpf(to, from, fac);
r = POINTER_OFFSET(r, stride);
}
}
static void gpencil_interpolate_v4_from_to(
float from[4], float to[4], float *point_offset, int it, int stride)
{
/* smooth interpolation */
float *r = point_offset;
for (int i = 0; i <= it; i++) {
float fac = (float)i / (float)it;
fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; // smooth
interp_v4_v4v4(r, from, to, fac);
r = POINTER_OFFSET(r, stride);
}
}
static float gpencil_approximate_curve_segment_arclength(bGPDcurve_point *cpt_start,
bGPDcurve_point *cpt_end)
{
BezTriple *bezt_start = &cpt_start->bezt;
BezTriple *bezt_end = &cpt_end->bezt;
float chord_len = len_v3v3(bezt_start->vec[1], bezt_end->vec[1]);
float net_len = len_v3v3(bezt_start->vec[1], bezt_start->vec[2]);
net_len += len_v3v3(bezt_start->vec[2], bezt_end->vec[0]);
net_len += len_v3v3(bezt_end->vec[0], bezt_end->vec[1]);
return (chord_len + net_len) / 2.0f;
}
static void gpencil_calculate_stroke_points_curve_segment(
bGPDcurve_point *cpt, bGPDcurve_point *cpt_next, float *points_offset, int resolu, int stride)
{
/* sample points on all 3 axis between two curve points */
for (uint axis = 0; axis < 3; axis++) {
BKE_curve_forward_diff_bezier(cpt->bezt.vec[1][axis],
cpt->bezt.vec[2][axis],
cpt_next->bezt.vec[0][axis],
cpt_next->bezt.vec[1][axis],
POINTER_OFFSET(points_offset, sizeof(float) * axis),
(int)resolu,
stride);
}
/* interpolate other attributes */
gpencil_interpolate_fl_from_to(cpt->pressure,
cpt_next->pressure,
POINTER_OFFSET(points_offset, sizeof(float) * 3),
resolu,
stride);
gpencil_interpolate_fl_from_to(cpt->strength,
cpt_next->strength,
POINTER_OFFSET(points_offset, sizeof(float) * 4),
resolu,
stride);
gpencil_interpolate_v4_from_to(cpt->vert_color,
cpt_next->vert_color,
POINTER_OFFSET(points_offset, sizeof(float) * 5),
resolu,
stride);
}
static float *gpencil_stroke_points_from_editcurve_adaptive_resolu(
bGPDcurve_point *curve_point_array,
int curve_point_array_len,
int resolution,
bool is_cyclic,
int *r_points_len)
{
/* One stride contains: x, y, z, pressure, strength, Vr, Vg, Vb, Vmix_factor */
const uint stride = sizeof(float[9]);
const uint cpt_last = curve_point_array_len - 1;
const uint num_segments = (is_cyclic) ? curve_point_array_len : curve_point_array_len - 1;
int *segment_point_lengths = MEM_callocN(sizeof(int) * num_segments, __func__);
uint points_len = 1;
for (int i = 0; i < cpt_last; i++) {
bGPDcurve_point *cpt = &curve_point_array[i];
bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
float arclen = gpencil_approximate_curve_segment_arclength(cpt, cpt_next);
int segment_resolu = (int)floorf(arclen * resolution);
CLAMP_MIN(segment_resolu, 1);
segment_point_lengths[i] = segment_resolu;
points_len += segment_resolu;
}
if (is_cyclic) {
bGPDcurve_point *cpt = &curve_point_array[cpt_last];
bGPDcurve_point *cpt_next = &curve_point_array[0];
float arclen = gpencil_approximate_curve_segment_arclength(cpt, cpt_next);
int segment_resolu = (int)floorf(arclen * resolution);
CLAMP_MIN(segment_resolu, 1);
segment_point_lengths[cpt_last] = segment_resolu;
points_len += segment_resolu;
}
float(*r_points)[9] = MEM_callocN((stride * points_len * (is_cyclic ? 2 : 1)), __func__);
float *points_offset = &r_points[0][0];
int point_index = 0;
for (int i = 0; i < cpt_last; i++) {
bGPDcurve_point *cpt_curr = &curve_point_array[i];
bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
int segment_resolu = segment_point_lengths[i];
gpencil_calculate_stroke_points_curve_segment(
cpt_curr, cpt_next, points_offset, segment_resolu, stride);
/* update the index */
cpt_curr->point_index = point_index;
point_index += segment_resolu;
points_offset = POINTER_OFFSET(points_offset, segment_resolu * stride);
}
bGPDcurve_point *cpt_curr = &curve_point_array[cpt_last];
cpt_curr->point_index = point_index;
if (is_cyclic) {
bGPDcurve_point *cpt_next = &curve_point_array[0];
int segment_resolu = segment_point_lengths[cpt_last];
gpencil_calculate_stroke_points_curve_segment(
cpt_curr, cpt_next, points_offset, segment_resolu, stride);
}
MEM_freeN(segment_point_lengths);
*r_points_len = points_len;
return (float(*))r_points;
}
/**
* Helper: calculate the points on a curve with a fixed resolution.
*/
static float *gpencil_stroke_points_from_editcurve_fixed_resolu(bGPDcurve_point *curve_point_array,
int curve_point_array_len,
int resolution,
bool is_cyclic,
int *r_points_len)
{
/* One stride contains: x, y, z, pressure, strength, Vr, Vg, Vb, Vmix_factor */
const uint stride = sizeof(float[9]);
const uint array_last = curve_point_array_len - 1;
const uint resolu_stride = resolution * stride;
const uint points_len = BKE_curve_calc_coords_axis_len(
curve_point_array_len, resolution, is_cyclic, false);
float(*r_points)[9] = MEM_callocN((stride * points_len * (is_cyclic ? 2 : 1)), __func__);
float *points_offset = &r_points[0][0];
for (unsigned int i = 0; i < array_last; i++) {
bGPDcurve_point *cpt_curr = &curve_point_array[i];
bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
gpencil_calculate_stroke_points_curve_segment(
cpt_curr, cpt_next, points_offset, resolution, stride);
/* update the index */
cpt_curr->point_index = i * resolution;
points_offset = POINTER_OFFSET(points_offset, resolu_stride);
}
bGPDcurve_point *cpt_curr = &curve_point_array[array_last];
cpt_curr->point_index = array_last * resolution;
if (is_cyclic) {
bGPDcurve_point *cpt_next = &curve_point_array[0];
gpencil_calculate_stroke_points_curve_segment(
cpt_curr, cpt_next, points_offset, resolution, stride);
}
*r_points_len = points_len;
return (float(*))r_points;
}
/**
* Recalculate stroke points with the editcurve of the stroke.
*/
void BKE_gpencil_stroke_update_geometry_from_editcurve(bGPDstroke *gps,
const uint resolution,
const bool adaptive)
{
if (gps == NULL || gps->editcurve == NULL) {
return;
}
bGPDcurve *editcurve = gps->editcurve;
bGPDcurve_point *curve_point_array = editcurve->curve_points;
int curve_point_array_len = editcurve->tot_curve_points;
if (curve_point_array_len == 0) {
return;
}
/* Handle case for single curve point. */
if (curve_point_array_len == 1) {
bGPDcurve_point *cpt = &curve_point_array[0];
/* resize stroke point array */
gps->totpoints = 1;
gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
if (gps->dvert != NULL) {
gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
}
bGPDspoint *pt = &gps->points[0];
copy_v3_v3(&pt->x, cpt->bezt.vec[1]);
pt->pressure = cpt->pressure;
pt->strength = cpt->strength;
copy_v4_v4(pt->vert_color, cpt->vert_color);
/* deselect */
pt->flag &= ~GP_SPOINT_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
return;
}
bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
int points_len = 0;
float(*points)[9] = NULL;
if (adaptive) {
points = (float(*)[9])gpencil_stroke_points_from_editcurve_adaptive_resolu(
curve_point_array, curve_point_array_len, resolution, is_cyclic, &points_len);
}
else {
points = (float(*)[9])gpencil_stroke_points_from_editcurve_fixed_resolu(
curve_point_array, curve_point_array_len, resolution, is_cyclic, &points_len);
}
if (points == NULL || points_len == 0) {
return;
}
/* resize stroke point array */
gps->totpoints = points_len;
gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
if (gps->dvert != NULL) {
gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
}
/* write new data to stroke point array */
for (int i = 0; i < points_len; i++) {
bGPDspoint *pt = &gps->points[i];
copy_v3_v3(&pt->x, &points[i][0]);
pt->pressure = points[i][3];
pt->strength = points[i][4];
copy_v4_v4(pt->vert_color, &points[i][5]);
/* deselect points */
pt->flag &= ~GP_SPOINT_SELECT;
}
gps->flag &= ~GP_STROKE_SELECT;
/* free temp data */
MEM_freeN(points);
}
/**
* Recalculate the handles of the edit curve of a grease pencil stroke
*/
void BKE_gpencil_editcurve_recalculate_handles(bGPDstroke *gps)
{
if (gps == NULL || gps->editcurve == NULL) {
return;
}
bool changed = false;
bGPDcurve *gpc = gps->editcurve;
if (gpc->tot_curve_points < 2) {
return;
}
if (gpc->tot_curve_points == 1) {
BKE_nurb_handle_calc(
&(gpc->curve_points[0].bezt), NULL, &(gpc->curve_points[0].bezt), false, 0);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
}
for (int i = 1; i < gpc->tot_curve_points - 1; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
bGPDcurve_point *gpc_pt_prev = &gpc->curve_points[i - 1];
bGPDcurve_point *gpc_pt_next = &gpc->curve_points[i + 1];
/* update handle if point or neighbour is selected */
if (gpc_pt->flag & GP_CURVE_POINT_SELECT || gpc_pt_prev->flag & GP_CURVE_POINT_SELECT ||
gpc_pt_next->flag & GP_CURVE_POINT_SELECT) {
BezTriple *bezt = &gpc_pt->bezt;
BezTriple *bezt_prev = &gpc_pt_prev->bezt;
BezTriple *bezt_next = &gpc_pt_next->bezt;
BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, false, 0);
changed = true;
}
}
bGPDcurve_point *gpc_first = &gpc->curve_points[0];
bGPDcurve_point *gpc_last = &gpc->curve_points[gpc->tot_curve_points - 1];
bGPDcurve_point *gpc_first_next = &gpc->curve_points[1];
bGPDcurve_point *gpc_last_prev = &gpc->curve_points[gpc->tot_curve_points - 2];
if (gps->flag & GP_STROKE_CYCLIC) {
if (gpc_first->flag & GP_CURVE_POINT_SELECT || gpc_last->flag & GP_CURVE_POINT_SELECT) {
BezTriple *bezt_first = &gpc_first->bezt;
BezTriple *bezt_last = &gpc_last->bezt;
BezTriple *bezt_first_next = &gpc_first_next->bezt;
BezTriple *bezt_last_prev = &gpc_last_prev->bezt;
BKE_nurb_handle_calc(bezt_first, bezt_last, bezt_first_next, false, 0);
BKE_nurb_handle_calc(bezt_last, bezt_last_prev, bezt_first, false, 0);
changed = true;
}
}
else {
if (gpc_first->flag & GP_CURVE_POINT_SELECT || gpc_last->flag & GP_CURVE_POINT_SELECT) {
BezTriple *bezt_first = &gpc_first->bezt;
BezTriple *bezt_last = &gpc_last->bezt;
BezTriple *bezt_first_next = &gpc_first_next->bezt;
BezTriple *bezt_last_prev = &gpc_last_prev->bezt;
BKE_nurb_handle_calc(bezt_first, NULL, bezt_first_next, false, 0);
BKE_nurb_handle_calc(bezt_last, bezt_last_prev, NULL, false, 0);
changed = true;
}
}
if (changed) {
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
}
}
/* Helper: count how many new curve points must be generated. */
static int gpencil_editcurve_subdivide_count(bGPDcurve *gpc, bool is_cyclic)
{
int count = 0;
for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
bGPDcurve_point *cpt = &gpc->curve_points[i];
bGPDcurve_point *cpt_next = &gpc->curve_points[i + 1];
if (cpt->flag & GP_CURVE_POINT_SELECT && cpt_next->flag & GP_CURVE_POINT_SELECT) {
count++;
}
}
if (is_cyclic) {
bGPDcurve_point *cpt = &gpc->curve_points[0];
bGPDcurve_point *cpt_next = &gpc->curve_points[gpc->tot_curve_points - 1];
if (cpt->flag & GP_CURVE_POINT_SELECT && cpt_next->flag & GP_CURVE_POINT_SELECT) {
count++;
}
}
return count;
}
static void gpencil_editcurve_subdivide_curve_segment(bGPDcurve_point *cpt_start,
bGPDcurve_point *cpt_end,
bGPDcurve_point *cpt_new)
{
BezTriple *bezt_start = &cpt_start->bezt;
BezTriple *bezt_end = &cpt_end->bezt;
BezTriple *bezt_new = &cpt_new->bezt;
for (int axis = 0; axis < 3; axis++) {
float p0, p1, p2, p3, m0, m1, q0, q1, b;
p0 = bezt_start->vec[1][axis];
p1 = bezt_start->vec[2][axis];
p2 = bezt_end->vec[0][axis];
p3 = bezt_end->vec[1][axis];
m0 = (p0 + p1) / 2;
q0 = (p0 + 2 * p1 + p2) / 4;
b = (p0 + 3 * p1 + 3 * p2 + p3) / 8;
q1 = (p1 + 2 * p2 + p3) / 4;
m1 = (p2 + p3) / 2;
bezt_new->vec[0][axis] = q0;
bezt_new->vec[2][axis] = q1;
bezt_new->vec[1][axis] = b;
bezt_start->vec[2][axis] = m0;
bezt_end->vec[0][axis] = m1;
}
cpt_new->pressure = interpf(cpt_end->pressure, cpt_start->pressure, 0.5f);
cpt_new->strength = interpf(cpt_end->strength, cpt_start->strength, 0.5f);
interp_v4_v4v4(cpt_new->vert_color, cpt_start->vert_color, cpt_end->vert_color, 0.5f);
}
void BKE_gpencil_editcurve_subdivide(bGPDstroke *gps, const int cuts)
{
bGPDcurve *gpc = gps->editcurve;
if (gpc == NULL || gpc->tot_curve_points < 2) {
return;
}
bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
/* repeat for number of cuts */
for (int s = 0; s < cuts; s++) {
int old_tot_curve_points = gpc->tot_curve_points;
int new_num_curve_points = gpencil_editcurve_subdivide_count(gpc, is_cyclic);
if (new_num_curve_points == 0) {
break;
}
int new_tot_curve_points = old_tot_curve_points + new_num_curve_points;
bGPDcurve_point *temp_curve_points = (bGPDcurve_point *)MEM_callocN(
sizeof(bGPDcurve_point) * new_tot_curve_points, __func__);
bool prev_subdivided = false;
int j = 0;
for (int i = 0; i < old_tot_curve_points - 1; i++, j++) {
bGPDcurve_point *cpt = &gpc->curve_points[i];
bGPDcurve_point *cpt_next = &gpc->curve_points[i + 1];
if (cpt->flag & GP_CURVE_POINT_SELECT && cpt_next->flag & GP_CURVE_POINT_SELECT) {
bGPDcurve_point *cpt_new = &temp_curve_points[j + 1];
gpencil_editcurve_subdivide_curve_segment(cpt, cpt_next, cpt_new);
memcpy(&temp_curve_points[j], cpt, sizeof(bGPDcurve_point));
memcpy(&temp_curve_points[j + 2], cpt_next, sizeof(bGPDcurve_point));
cpt_new->flag |= GP_CURVE_POINT_SELECT;
cpt_new->bezt.h1 = HD_ALIGN;
cpt_new->bezt.h2 = HD_ALIGN;
BEZT_SEL_ALL(&cpt_new->bezt);
prev_subdivided = true;
j++;
}
else if (!prev_subdivided) {
memcpy(&temp_curve_points[j], cpt, sizeof(bGPDcurve_point));
prev_subdivided = false;
}
else {
prev_subdivided = false;
}
}
if (is_cyclic) {
bGPDcurve_point *cpt = &gpc->curve_points[old_tot_curve_points - 1];
bGPDcurve_point *cpt_next = &gpc->curve_points[0];
if (cpt->flag & GP_CURVE_POINT_SELECT && cpt_next->flag & GP_CURVE_POINT_SELECT) {
bGPDcurve_point *cpt_new = &temp_curve_points[j + 1];
gpencil_editcurve_subdivide_curve_segment(cpt, cpt_next, cpt_new);
memcpy(&temp_curve_points[j], cpt, sizeof(bGPDcurve_point));
memcpy(&temp_curve_points[0], cpt_next, sizeof(bGPDcurve_point));
cpt_new->flag |= GP_CURVE_POINT_SELECT;
cpt_new->bezt.h1 = HD_ALIGN;
cpt_new->bezt.h2 = HD_ALIGN;
BEZT_SEL_ALL(&cpt_new->bezt);
}
else if (!prev_subdivided) {
memcpy(&temp_curve_points[j], cpt, sizeof(bGPDcurve_point));
}
}
else {
bGPDcurve_point *cpt = &gpc->curve_points[old_tot_curve_points - 1];
memcpy(&temp_curve_points[j], cpt, sizeof(bGPDcurve_point));
}
MEM_freeN(gpc->curve_points);
gpc->curve_points = temp_curve_points;
gpc->tot_curve_points = new_tot_curve_points;
}
}
void BKE_gpencil_strokes_selected_update_editcurve(bGPdata *gpd)
{
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* For all selected strokes, update edit curve. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (!BKE_gpencil_layer_is_editable(gpl)) {
continue;
}
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && is_multiedit)) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* skip deselected stroke */
if (!(gps->flag & GP_STROKE_SELECT)) {
continue;
}
/* Generate the curve if there is none or the stroke was changed */
if (gps->editcurve == NULL) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
/* Continue if curve could not be generated. */
if (gps->editcurve == NULL) {
continue;
}
}
else if (gps->editcurve->flag & GP_CURVE_NEEDS_STROKE_UPDATE) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
}
/* Update the selection from the stroke to the curve. */
BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
}
}
void BKE_gpencil_strokes_selected_sync_selection_editcurve(bGPdata *gpd)
{
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* Sync selection for all strokes with editcurve. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (!BKE_gpencil_layer_is_editable(gpl)) {
continue;
}
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && is_multiedit)) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
bGPDcurve *gpc = gps->editcurve;
if (gpc != NULL) {
/* Update the selection of every stroke that has an editcurve */
BKE_gpencil_stroke_editcurve_sync_selection(gps, gpc);
}
}
}
}
}
}
/** \} */

View File

@ -45,8 +45,11 @@
#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
#include "BLT_translation.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_main.h"
#include "BKE_material.h"
@ -415,10 +418,11 @@ static void stroke_interpolate_deform_weights(
/**
* Resample a stroke
* \param gpd: Grease pencil data-block
* \param gps: Stroke to sample
* \param dist: Distance of one segment
*/
bool BKE_gpencil_stroke_sample(bGPDstroke *gps, const float dist, const bool select)
bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select)
{
bGPDspoint *pt = gps->points;
bGPDspoint *pt1 = NULL;
@ -515,7 +519,7 @@ bool BKE_gpencil_stroke_sample(bGPDstroke *gps, const float dist, const bool sel
gps->totpoints = i;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
return true;
}
@ -628,13 +632,15 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const
/**
* Split stroke.
* \param gpd: Grease pencil data-block
* \param gpf: Grease pencil frame
* \param gps: Grease pencil original stroke
* \param before_index: Position of the point to split
* \param remaining_gps: Secondary stroke after split.
* \return True if the split was done
*/
bool BKE_gpencil_stroke_split(bGPDframe *gpf,
bool BKE_gpencil_stroke_split(bGPdata *gpd,
bGPDframe *gpf,
bGPDstroke *gps,
const int before_index,
bGPDstroke **remaining_gps)
@ -684,7 +690,7 @@ bool BKE_gpencil_stroke_split(bGPDframe *gpf,
* Keep the end point. */
BKE_gpencil_stroke_trim_points(gps, 0, old_count);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
return true;
}
@ -1273,14 +1279,31 @@ void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
/**
* Recalc all internal geometry data for the stroke
* \param gpd: Grease pencil data-block
* \param gps: Grease pencil stroke
*/
void BKE_gpencil_stroke_geometry_update(bGPDstroke *gps)
void BKE_gpencil_stroke_geometry_update(bGPdata *gpd, bGPDstroke *gps)
{
if (gps == NULL) {
return;
}
if (gps->editcurve != NULL) {
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
/* curve geometry was updated: stroke needs recalculation */
if (gps->flag & GP_STROKE_NEEDS_CURVE_UPDATE) {
bool is_adaptive = gpd->flag & GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
BKE_gpencil_stroke_update_geometry_from_editcurve(
gps, gpd->curve_edit_resolution, is_adaptive);
gps->flag &= ~GP_STROKE_NEEDS_CURVE_UPDATE;
}
}
else {
/* stroke geometry was updated: editcurve needs recalculation */
gps->editcurve->flag |= GP_CURVE_NEEDS_STROKE_UPDATE;
}
}
if (gps->totpoints > 2) {
BKE_gpencil_stroke_fill_triangulate(gps);
}
@ -1326,7 +1349,7 @@ float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
* Trim stroke to the first intersection or loop.
* \param gps: Stroke data
*/
bool BKE_gpencil_stroke_trim(bGPDstroke *gps)
bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps)
{
if (gps->totpoints < 4) {
return false;
@ -1413,7 +1436,7 @@ bool BKE_gpencil_stroke_trim(bGPDstroke *gps)
MEM_SAFE_FREE(old_dvert);
}
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
return intersect;
}
@ -1509,11 +1532,12 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps)
/**
* Dissolve points in stroke.
* \param gpd: Grease pencil data-block
* \param gpf: Grease pencil frame
* \param gps: Grease pencil stroke
* \param tag: Type of tag for point
*/
void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short tag)
void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const short tag)
{
bGPDspoint *pt;
MDeformVert *dvert = NULL;
@ -1589,7 +1613,7 @@ void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short ta
gps->totpoints = tot;
/* triangles cache needs to be recalculated */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
@ -1635,10 +1659,11 @@ void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
*
* Ramer - Douglas - Peucker algorithm
* by http ://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
* \param gpd: Grease pencil data-block
* \param gps: Grease pencil stroke
* \param epsilon: Epsilon value to define precision of the algorithm
*/
void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon)
void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float epsilon)
{
bGPDspoint *old_points = MEM_dupallocN(gps->points);
int totpoints = gps->totpoints;
@ -1735,7 +1760,7 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon)
gps->totpoints = j;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
MEM_SAFE_FREE(old_points);
MEM_SAFE_FREE(old_dvert);
@ -1744,9 +1769,10 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPDstroke *gps, float epsilon)
/**
* Simplify alternate vertex of stroke except extremes.
* \param gpd: Grease pencil data-block
* \param gps: Grease pencil stroke
*/
void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps)
void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
{
if (gps->totpoints < 5) {
return;
@ -1800,19 +1826,20 @@ void BKE_gpencil_stroke_simplify_fixed(bGPDstroke *gps)
gps->totpoints = j;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
MEM_SAFE_FREE(old_points);
MEM_SAFE_FREE(old_dvert);
}
/**
* Subdivide grease pencil stroke.
* \param gps: Grease pencil stroke
* Subdivide a stroke
* \param gpd: Grease pencil data-block
* \param gps: Stroke
* \param level: Level of subdivision
* \param type: Type of subdivision
*/
void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type)
void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int type)
{
bGPDspoint *temp_points;
MDeformVert *temp_dverts = NULL;
@ -1921,7 +1948,7 @@ void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type)
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
/* Merge by distance ------------------------------------- */
@ -1930,12 +1957,14 @@ void BKE_gpencil_stroke_subdivide(bGPDstroke *gps, int level, int type)
* Reduce a series of points when the distance is below a threshold.
* Special case for first and last points (both are keeped) for other points,
* the merge point always is at first point.
* \param gpd: Grease pencil data-block
* \param gpf: Grease Pencil frame
* \param gps: Grease Pencil stroke
* \param threshold: Distance between points
* \param use_unselected: Set to true to analyze all stroke and not only selected points
*/
void BKE_gpencil_stroke_merge_distance(bGPDframe *gpf,
void BKE_gpencil_stroke_merge_distance(bGPdata *gpd,
bGPDframe *gpf,
bGPDstroke *gps,
const float threshold,
const bool use_unselected)
@ -2000,11 +2029,11 @@ void BKE_gpencil_stroke_merge_distance(bGPDframe *gpf,
/* Dissolve tagged points */
if (tagged) {
BKE_gpencil_dissolve_points(gpf, gps, GP_SPOINT_TAG);
BKE_gpencil_dissolve_points(gpd, gpf, gps, GP_SPOINT_TAG);
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
typedef struct GpEdge {
@ -2093,6 +2122,7 @@ static int gpencil_walk_edge(GHash *v_table,
}
static void gpencil_generate_edgeloops(Object *ob,
bGPdata *gpd,
bGPDframe *gpf_stroke,
int stroke_mat_index,
const float angle,
@ -2218,7 +2248,7 @@ static void gpencil_generate_edgeloops(Object *ob,
pt->strength = 1.0f;
}
BKE_gpencil_stroke_geometry_update(gps_stroke);
BKE_gpencil_stroke_geometry_update(gpd, gps_stroke);
}
/* Free memory. */
@ -2397,10 +2427,10 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
}
/* If has only 3 points subdivide. */
if (mp->totloop == 3) {
BKE_gpencil_stroke_subdivide(gps_fill, 1, GP_SUBDIV_SIMPLE);
BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE);
}
BKE_gpencil_stroke_geometry_update(gps_fill);
BKE_gpencil_stroke_geometry_update(gpd, gps_fill);
}
}
}
@ -2417,7 +2447,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
gpencil_generate_edgeloops(
ob_eval, gpf_stroke, stroke_mat_index, angle, thickness, offset, matrix, use_seams);
ob_eval, gpd, gpf_stroke, stroke_mat_index, angle, thickness, offset, matrix, use_seams);
/* Tag for recalculation */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
@ -2457,7 +2487,7 @@ void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
}
/* Distortion may mean we need to re-triangulate. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
@ -2549,7 +2579,7 @@ void BKE_gpencil_point_coords_apply(bGPdata *gpd, const GPencilPointCoordinates
}
/* Distortion may mean we need to re-triangulate. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
@ -2586,7 +2616,7 @@ void BKE_gpencil_point_coords_apply_with_mat4(bGPdata *gpd,
}
/* Distortion may mean we need to re-triangulate. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}

View File

@ -698,7 +698,9 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o
}
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_eval);
const bool do_modifiers = (bool)((!is_multiedit) && (ob->greasepencil_modifiers.first != NULL) &&
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_eval);
const bool do_modifiers = (bool)((!is_multiedit) && (!is_curve_edit) &&
(ob->greasepencil_modifiers.first != NULL) &&
(!GPENCIL_SIMPLIFY_MODIF(scene)));
if ((!do_modifiers) && (!do_parent)) {
return;
@ -734,8 +736,10 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
bGPdata *gpd = (bGPdata *)ob->data;
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const bool do_modifiers = (bool)((!is_multiedit) && (ob->greasepencil_modifiers.first != NULL) &&
const bool do_modifiers = (bool)((!is_multiedit) && (!is_curve_edit) &&
(ob->greasepencil_modifiers.first != NULL) &&
(!GPENCIL_SIMPLIFY_MODIF(scene)));
if (!do_modifiers) {
return;

View File

@ -4871,7 +4871,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
gps->fill_opacity_fac = 1.0f;
/* Calc geometry data because in old versions this data was not saved. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill);
int i;

View File

@ -1067,6 +1067,20 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
part->phystype = PART_PHYS_NO;
}
}
/* Init grease pencil default curve resolution. */
if (!DNA_struct_elem_find(fd->filesdna, "bGPdata", "int", "curve_edit_resolution")) {
LISTBASE_FOREACH (bGPdata *, gpd, &bmain->gpencils) {
gpd->curve_edit_resolution = GP_DEFAULT_CURVE_RESOLUTION;
gpd->flag |= GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
}
}
/* Init grease pencil curve editing error threshold. */
if (!DNA_struct_elem_find(fd->filesdna, "bGPdata", "float", "curve_edit_threshold")) {
LISTBASE_FOREACH (bGPdata *, gpd, &bmain->gpencils) {
gpd->curve_edit_threshold = GP_DEFAULT_CURVE_ERROR;
gpd->curve_edit_corner_angle = GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE;
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 291, 9)) {

View File

@ -49,6 +49,10 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata)
pd->edit_gpencil_wires_grp = NULL;
psl->edit_gpencil_ps = NULL;
pd->edit_gpencil_curve_handle_grp = NULL;
pd->edit_gpencil_curve_points_grp = NULL;
psl->edit_gpencil_curve_ps = NULL;
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
Object *ob = draw_ctx->obact;
@ -105,7 +109,8 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata)
(GPENCIL_EDIT_MODE(gpd) &&
(ts->gpencil_selectmode_edit != GP_SELECTMODE_STROKE));
if ((!GPENCIL_VERTEX_MODE(gpd) && !GPENCIL_PAINT_MODE(gpd)) || use_vertex_mask) {
if ((!GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) &&
((!GPENCIL_VERTEX_MODE(gpd) && !GPENCIL_PAINT_MODE(gpd)) || use_vertex_mask)) {
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
DRW_STATE_BLEND_ALPHA;
DRW_PASS_CREATE(psl->edit_gpencil_ps, state | pd->clipping_state);
@ -132,6 +137,37 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata)
}
}
/* Handles and curve point for Curve Edit submode. */
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
DRWState state = DRW_STATE_WRITE_COLOR;
DRW_PASS_CREATE(psl->edit_gpencil_curve_ps, state | pd->clipping_state);
/* Edit lines. */
if (show_lines) {
sh = OVERLAY_shader_edit_gpencil_wire();
pd->edit_gpencil_wires_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_bool_copy(grp, "doMultiframe", show_multi_edit_lines);
DRW_shgroup_uniform_bool_copy(grp, "doWeightColor", is_weight_paint);
DRW_shgroup_uniform_bool_copy(grp, "hideSelect", hide_select);
DRW_shgroup_uniform_float_copy(grp, "gpEditOpacity", v3d->vertex_opacity);
DRW_shgroup_uniform_texture(grp, "weightTex", G_draw.weight_ramp);
}
sh = OVERLAY_shader_edit_curve_handle();
pd->edit_gpencil_curve_handle_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_bool_copy(grp, "showCurveHandles", pd->edit_curve.show_handles);
DRW_shgroup_uniform_int_copy(grp, "curveHandleDisplay", pd->edit_curve.handle_display);
DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA);
sh = OVERLAY_shader_edit_curve_point();
pd->edit_gpencil_curve_points_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_bool_copy(grp, "showCurveHandles", pd->edit_curve.show_handles);
DRW_shgroup_uniform_int_copy(grp, "curveHandleDisplay", pd->edit_curve.handle_display);
}
/* control points for primitives and speed guide */
const bool is_cppoint = (gpd->runtime.tot_cp_points > 0);
const bool is_speed_guide = (ts->gp_sculpt.guide.use_guide &&
@ -182,6 +218,7 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata)
void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata)
{
OVERLAY_PassList *psl = vedata->psl;
OVERLAY_PrivateData *pd = vedata->stl->pd;
struct GPUShader *sh;
DRWShadingGroup *grp;
@ -196,6 +233,9 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata)
ToolSettings *ts = scene->toolsettings;
const View3DCursor *cursor = &scene->cursor;
pd->edit_curve.show_handles = v3d->overlay.handle_display != CURVE_HANDLE_NONE;
pd->edit_curve.handle_display = v3d->overlay.handle_display;
if (gpd == NULL || ob->type != OB_GPENCIL) {
return;
}
@ -303,6 +343,20 @@ static void OVERLAY_edit_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob
struct GPUBatch *geom = DRW_cache_gpencil_edit_points_get(ob, pd->cfra);
DRW_shgroup_call_no_cull(grp, geom, ob);
}
if (pd->edit_gpencil_curve_handle_grp) {
struct GPUBatch *geom = DRW_cache_gpencil_edit_curve_handles_get(ob, pd->cfra);
if (geom) {
DRW_shgroup_call_no_cull(pd->edit_gpencil_curve_handle_grp, geom, ob);
}
}
if (pd->edit_gpencil_curve_points_grp) {
struct GPUBatch *geom = DRW_cache_gpencil_edit_curve_points_get(ob, pd->cfra);
if (geom) {
DRW_shgroup_call_no_cull(pd->edit_gpencil_curve_points_grp, geom, ob);
}
}
}
static void overlay_gpencil_draw_stroke_color_name(bGPDlayer *UNUSED(gpl),
@ -407,4 +461,9 @@ void OVERLAY_edit_gpencil_draw(OVERLAY_Data *vedata)
if (psl->edit_gpencil_ps) {
DRW_draw_pass(psl->edit_gpencil_ps);
}
/* Curve edit handles. */
if (psl->edit_gpencil_curve_ps) {
DRW_draw_pass(psl->edit_gpencil_curve_ps);
}
}

View File

@ -77,6 +77,7 @@ typedef struct OVERLAY_PassList {
DRWPass *edit_curve_handle_ps;
DRWPass *edit_gpencil_ps;
DRWPass *edit_gpencil_gizmos_ps;
DRWPass *edit_gpencil_curve_ps;
DRWPass *edit_lattice_ps;
DRWPass *edit_mesh_depth_ps[2];
DRWPass *edit_mesh_verts_ps[2];
@ -252,6 +253,8 @@ typedef struct OVERLAY_PrivateData {
DRWShadingGroup *edit_lattice_wires_grp;
DRWShadingGroup *edit_gpencil_points_grp;
DRWShadingGroup *edit_gpencil_wires_grp;
DRWShadingGroup *edit_gpencil_curve_handle_grp;
DRWShadingGroup *edit_gpencil_curve_points_grp;
DRWShadingGroup *edit_mesh_depth_grp[2];
DRWShadingGroup *edit_mesh_faces_grp[2];
DRWShadingGroup *edit_mesh_faces_cage_grp[2];

View File

@ -53,6 +53,9 @@ void main()
bool edge_selected = (((vertFlag[1] | vertFlag[0]) & VERT_SELECTED) != 0);
bool handle_selected = (showCurveHandles &&
(((vertFlag[1] | vertFlag[0]) & VERT_SELECTED_BEZT_HANDLE) != 0));
/* It reuses freestyle flag because the flag is 8 bits and all are already used and this
* flag is not used in this context. */
bool is_gpencil = ((vertFlag[1] & EDGE_FREESTYLE) != 0);
/* If handle type is only selected and the edge is not selected, don't show. */
if ((curveHandleDisplay != CURVE_HANDLE_ALL) && (!handle_selected)) {
@ -61,6 +64,9 @@ void main()
if ((!is_u_segment) && (color_id <= 4)) {
return;
}
if (is_gpencil) {
return;
}
}
vec4 inner_color;

View File

@ -17,16 +17,17 @@ void main()
{
GPU_INTEL_VERTEX_SHADER_WORKAROUND
/* Reuse the FREESTYLE flag to determine is GPencil. */
if ((data & VERT_SELECTED) != 0) {
if ((data & VERT_ACTIVE) != 0) {
finalColor = colorEditMeshActive;
}
else {
finalColor = colorVertexSelect;
finalColor = ((data & EDGE_FREESTYLE) == 0) ? colorVertexSelect : colorGpencilVertexSelect;
}
}
else {
finalColor = colorVertex;
finalColor = ((data & EDGE_FREESTYLE) == 0) ? colorVertex : colorGpencilVertex;
}
vec3 world_pos = point_object_to_world(pos);

View File

@ -253,6 +253,8 @@ struct GPUBatch *DRW_cache_gpencil_strokes_get(struct Object *ob, int cfra);
struct GPUBatch *DRW_cache_gpencil_fills_get(struct Object *ob, int cfra);
struct GPUBatch *DRW_cache_gpencil_edit_lines_get(struct Object *ob, int cfra);
struct GPUBatch *DRW_cache_gpencil_edit_points_get(struct Object *ob, int cfra);
struct GPUBatch *DRW_cache_gpencil_edit_curve_handles_get(struct Object *ob, int cfra);
struct GPUBatch *DRW_cache_gpencil_edit_curve_points_get(struct Object *ob, int cfra);
struct GPUBatch *DRW_cache_gpencil_sbuffer_stroke_get(struct Object *ob);
struct GPUBatch *DRW_cache_gpencil_sbuffer_fill_get(struct Object *ob);

View File

@ -21,6 +21,7 @@
* \ingroup draw
*/
#include "DNA_curve_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_screen_types.h"
@ -43,6 +44,9 @@
#include "draw_cache.h"
#include "draw_cache_impl.h"
#define BEZIER_HANDLE 1 << 3
#define COLOR_SHIFT 5
/* ---------------------------------------------------------------------- */
typedef struct GpencilBatchCache {
/** Instancing Data */
@ -59,6 +63,10 @@ typedef struct GpencilBatchCache {
GPUVertBuf *edit_vbo;
GPUBatch *edit_lines_batch;
GPUBatch *edit_points_batch;
/** Edit Curve Mode */
GPUVertBuf *edit_curve_vbo;
GPUBatch *edit_curve_handles_batch;
GPUBatch *edit_curve_points_batch;
/** Cache is dirty */
bool is_dirty;
@ -123,6 +131,10 @@ static void gpencil_batch_cache_clear(GpencilBatchCache *cache)
GPU_BATCH_DISCARD_SAFE(cache->edit_points_batch);
GPU_VERTBUF_DISCARD_SAFE(cache->edit_vbo);
GPU_BATCH_DISCARD_SAFE(cache->edit_curve_handles_batch);
GPU_BATCH_DISCARD_SAFE(cache->edit_curve_points_batch);
GPU_VERTBUF_DISCARD_SAFE(cache->edit_curve_vbo);
cache->is_dirty = true;
}
@ -196,6 +208,23 @@ static GPUVertFormat *gpencil_edit_stroke_format(void)
return &format;
}
/* MUST match the format below. */
typedef struct gpEditCurveVert {
float pos[3];
int data;
} gpEditCurveVert;
static GPUVertFormat *gpencil_edit_curve_format(void)
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
/* initialize vertex formats */
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "data", GPU_COMP_U8, 1, GPU_FETCH_INT);
}
return &format;
}
/* MUST match the format below. */
typedef struct gpColorVert {
float vcol[4]; /* Vertex color */
@ -228,6 +257,7 @@ typedef struct gpIterData {
GPUIndexBufBuilder ibo;
int vert_len;
int tri_len;
int curve_len;
} gpIterData;
static GPUVertBuf *gpencil_dummy_buffer_get(void)
@ -383,6 +413,7 @@ static void gpencil_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfr
.ibo = {0},
.vert_len = 1, /* Start at 1 for the gl_InstanceID trick to work (see vert shader). */
.tri_len = 0,
.curve_len = 0,
};
BKE_gpencil_visible_stroke_iter(
NULL, ob, NULL, gpencil_object_verts_count_cb, &iter, do_onion, cfra);
@ -653,6 +684,11 @@ typedef struct gpEditIterData {
int vgindex;
} gpEditIterData;
typedef struct gpEditCurveIterData {
gpEditCurveVert *verts;
int vgindex;
} gpEditCurveIterData;
static uint32_t gpencil_point_edit_flag(const bool layer_lock,
const bGPDspoint *pt,
int v,
@ -698,6 +734,92 @@ static void gpencil_edit_stroke_iter_cb(bGPDlayer *gpl,
vert_ptr->weight = gpencil_point_edit_weight(dvert, 0, iter->vgindex);
}
static void gpencil_edit_curve_stroke_count_cb(bGPDlayer *gpl,
bGPDframe *UNUSED(gpf),
bGPDstroke *gps,
void *thunk)
{
if (gpl->flag & GP_LAYER_LOCKED) {
return;
}
gpIterData *iter = (gpIterData *)thunk;
if (gps->editcurve == NULL) {
return;
}
/* Store first index offset */
gps->runtime.curve_start = iter->curve_len;
iter->curve_len += gps->editcurve->tot_curve_points * 4;
}
static char gpencil_beztriple_vflag_get(char flag,
char col_id,
bool handle_point,
const bool handle_selected)
{
char vflag = 0;
SET_FLAG_FROM_TEST(vflag, (flag & SELECT), VFLAG_VERT_SELECTED);
SET_FLAG_FROM_TEST(vflag, handle_point, BEZIER_HANDLE);
SET_FLAG_FROM_TEST(vflag, handle_selected, VFLAG_VERT_SELECTED_BEZT_HANDLE);
/* Reuse flag of Freestyle to indicate is GPencil data. */
vflag |= VFLAG_EDGE_FREESTYLE;
/* Handle color id. */
vflag |= col_id << COLOR_SHIFT;
return vflag;
}
static void gpencil_edit_curve_stroke_iter_cb(bGPDlayer *gpl,
bGPDframe *gpf,
bGPDstroke *gps,
void *thunk)
{
if (gpl->flag & GP_LAYER_LOCKED) {
return;
}
if (gps->editcurve == NULL) {
return;
}
bGPDcurve *editcurve = gps->editcurve;
gpEditCurveIterData *iter = (gpEditCurveIterData *)thunk;
const int v = gps->runtime.curve_start;
gpEditCurveVert *vert_ptr = iter->verts + v;
/* Hide points when the curve is unselected. Passing the control point
* as handle produces the point shader skip it if you are not in ALL mode. */
const bool hide = !(editcurve->flag & GP_CURVE_SELECT);
for (int i = 0; i < editcurve->tot_curve_points; i++) {
BezTriple *bezt = &editcurve->curve_points[i].bezt;
const bool handle_selected = BEZT_ISSEL_ANY(bezt);
const char vflag[3] = {
gpencil_beztriple_vflag_get(bezt->f1, bezt->h1, true, handle_selected),
gpencil_beztriple_vflag_get(bezt->f2, bezt->h1, hide, handle_selected),
gpencil_beztriple_vflag_get(bezt->f3, bezt->h2, true, handle_selected),
};
/* First segment. */
copy_v3_v3(vert_ptr->pos, bezt->vec[0]);
vert_ptr->data = vflag[0];
vert_ptr++;
copy_v3_v3(vert_ptr->pos, bezt->vec[1]);
vert_ptr->data = vflag[1];
vert_ptr++;
/* Second segment. */
copy_v3_v3(vert_ptr->pos, bezt->vec[1]);
vert_ptr->data = vflag[1];
vert_ptr++;
copy_v3_v3(vert_ptr->pos, bezt->vec[2]);
vert_ptr->data = vflag[2];
vert_ptr++;
}
}
static void gpencil_edit_batches_ensure(Object *ob, GpencilBatchCache *cache, int cfra)
{
bGPdata *gpd = (bGPdata *)ob->data;
@ -737,6 +859,46 @@ static void gpencil_edit_batches_ensure(Object *ob, GpencilBatchCache *cache, in
cache->edit_lines_batch = GPU_batch_create(GPU_PRIM_LINE_STRIP, cache->vbo, NULL);
GPU_batch_vertbuf_add(cache->edit_lines_batch, cache->edit_vbo);
}
/* Curve Handles and Points for Editing. */
if (cache->edit_curve_vbo == NULL) {
gpIterData iterdata = {
.gpd = gpd,
.verts = NULL,
.ibo = {0},
.vert_len = 0,
.tri_len = 0,
.curve_len = 0,
};
/* Create VBO. */
GPUVertFormat *format = gpencil_edit_curve_format();
cache->edit_curve_vbo = GPU_vertbuf_create_with_format(format);
/* Count data. */
BKE_gpencil_visible_stroke_iter(
NULL, ob, NULL, gpencil_edit_curve_stroke_count_cb, &iterdata, false, cfra);
gpEditCurveIterData iter;
int vert_len = iterdata.curve_len;
if (vert_len > 0) {
GPU_vertbuf_data_alloc(cache->edit_curve_vbo, vert_len);
iter.verts = (gpEditCurveVert *)GPU_vertbuf_get_data(cache->edit_curve_vbo);
/* Fill buffers with data. */
BKE_gpencil_visible_stroke_iter(
NULL, ob, NULL, gpencil_edit_curve_stroke_iter_cb, &iter, false, cfra);
cache->edit_curve_handles_batch = GPU_batch_create(
GPU_PRIM_LINES, cache->edit_curve_vbo, NULL);
GPU_batch_vertbuf_add(cache->edit_curve_handles_batch, cache->edit_curve_vbo);
cache->edit_curve_points_batch = GPU_batch_create(
GPU_PRIM_POINTS, cache->edit_curve_vbo, NULL);
GPU_batch_vertbuf_add(cache->edit_curve_points_batch, cache->edit_curve_vbo);
}
gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY;
cache->is_dirty = false;
@ -761,4 +923,22 @@ GPUBatch *DRW_cache_gpencil_edit_points_get(Object *ob, int cfra)
return cache->edit_points_batch;
}
GPUBatch *DRW_cache_gpencil_edit_curve_handles_get(Object *ob, int cfra)
{
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
gpencil_batches_ensure(ob, cache, cfra);
gpencil_edit_batches_ensure(ob, cache, cfra);
return cache->edit_curve_handles_batch;
}
GPUBatch *DRW_cache_gpencil_edit_curve_points_get(Object *ob, int cfra)
{
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra);
gpencil_batches_ensure(ob, cache, cfra);
gpencil_edit_batches_ensure(ob, cache, cfra);
return cache->edit_curve_points_batch;
}
/** \} */

View File

@ -42,6 +42,7 @@ set(SRC
gpencil_convert.c
gpencil_data.c
gpencil_edit.c
gpencil_edit_curve.c
gpencil_fill.c
gpencil_interpolate.c
gpencil_merge.c

View File

@ -910,7 +910,7 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p)
int totarrowpoints = runtime.arrow_end_style;
/* Setting up arrow stroke. */
bGPDstroke *e_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false);
bGPDstroke *e_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false, false);
annotation_stroke_arrow_allocate(e_arrow_gps, totarrowpoints);
/* Set pointer to first non-initialized point. */
@ -931,7 +931,7 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p)
int totarrowpoints = runtime.arrow_start_style;
/* Setting up arrow stroke. */
bGPDstroke *s_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false);
bGPDstroke *s_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false, false);
annotation_stroke_arrow_allocate(s_arrow_gps, totarrowpoints);
/* Set pointer to first non-initialized point. */
@ -1198,7 +1198,7 @@ static void annotation_stroke_eraser_dostroke(tGPsdata *p,
/* Second Pass: Remove any points that are tagged */
if (do_cull) {
gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
gpencil_stroke_delete_tagged_points(p->gpd, gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
}
}
}

View File

@ -486,7 +486,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode)
*/
for (gps = gpfs->strokes.first; gps; gps = gps->next) {
/* make a copy of stroke, then of its points array */
gpsn = BKE_gpencil_stroke_duplicate(gps, true);
gpsn = BKE_gpencil_stroke_duplicate(gps, true, true);
/* append stroke to frame */
BLI_addtail(&gpf->strokes, gpsn);

View File

@ -861,115 +861,115 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4])
/* generate strokes */
gps = BKE_gpencil_stroke_add(frameFills, color_Skin, 270, 75, false);
BKE_gpencil_stroke_add_points(gps, data0, 270, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data1, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 60, false);
BKE_gpencil_stroke_add_points(gps, data2, 18, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data3, 64, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data4, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data5, 64, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data6, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data7, 18, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false);
BKE_gpencil_stroke_add_points(gps, data8, 49, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data9, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false);
BKE_gpencil_stroke_add_points(gps, data10, 49, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data11, 18, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data12, 18, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data13, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data14, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 65, 60, false);
BKE_gpencil_stroke_add_points(gps, data15, 65, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 60, false);
BKE_gpencil_stroke_add_points(gps, data16, 34, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data17, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 40, false);
BKE_gpencil_stroke_add_points(gps, data18, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 40, false);
BKE_gpencil_stroke_add_points(gps, data19, 34, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data20, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data21, 64, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false);
BKE_gpencil_stroke_add_points(gps, data22, 26, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false);
BKE_gpencil_stroke_add_points(gps, data23, 26, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data24, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data25, 18, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data26, 18, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data27, 33, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
/* update depsgraph */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);

View File

@ -249,7 +249,7 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
/* generate stroke */
gps = BKE_gpencil_stroke_add(frame_lines, color_black, 175, 75, false);
BKE_gpencil_stroke_add_points(gps, data0, 175, mat);
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
/* update depsgraph */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);

View File

@ -1855,12 +1855,12 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op)
bGPdata *gpd = (bGPdata *)ob->data;
bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true);
bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA);
done = BKE_gpencil_from_image(sima, gpf, size, is_mask);
done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask);
if (done) {
/* Delete any selected point. */
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
gpencil_stroke_delete_tagged_points(gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
}
BKE_reportf(op->reports, RPT_INFO, "Object created");

View File

@ -580,7 +580,7 @@ static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) {
/* Make copy of source stroke. */
bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
/* Check if material is in destination object,
* otherwise add the slot with the material. */
@ -1531,6 +1531,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
const int direction = RNA_enum_get(op->ptr, "direction");
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
/* temp listbase to store selected strokes */
ListBase selected = {NULL};
@ -1589,7 +1590,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
break;
/* Bring Forward */
case GP_STROKE_MOVE_UP:
for (LinkData *link = selected.last; link; link = link->prev) {
LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
gps = link->data;
BLI_listbase_link_move(&gpf->strokes, gps, 1);
}
@ -1603,7 +1604,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
break;
/* Send to Back */
case GP_STROKE_MOVE_BOTTOM:
for (LinkData *link = selected.last; link; link = link->prev) {
LISTBASE_FOREACH_BACKWARD (LinkData *, link, &selected) {
gps = link->data;
BLI_remlink(&gpf->strokes, gps);
BLI_addhead(&gpf->strokes, gps);
@ -1612,6 +1613,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
default:
BLI_assert(0);
break;
changed = true;
}
}
BLI_freelistN(&selected);
@ -1625,9 +1627,11 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
if (changed) {
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
return OPERATOR_FINISHED;
}
@ -1693,6 +1697,7 @@ static int gpencil_stroke_change_color_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
bool changed = false;
/* loop all strokes */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
@ -1717,6 +1722,8 @@ static int gpencil_stroke_change_color_exec(bContext *C, wmOperator *op)
/* assign new color */
gps->mat_nr = idx;
changed = true;
}
}
}
@ -1728,9 +1735,11 @@ static int gpencil_stroke_change_color_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
if (changed) {
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
return OPERATOR_FINISHED;
}
@ -1757,9 +1766,7 @@ void GPENCIL_OT_stroke_change_color(wmOperatorType *ot)
static int gpencil_material_lock_unsused_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
short *totcol = BKE_object_material_len_p(ob);
/* sanity checks */
@ -1776,6 +1783,7 @@ static int gpencil_material_lock_unsused_exec(bContext *C, wmOperator *UNUSED(op
}
}
bool changed = false;
/* loop all selected strokes and unlock any color */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* only editable and visible layers are considered */
@ -1793,19 +1801,24 @@ static int gpencil_material_lock_unsused_exec(bContext *C, wmOperator *UNUSED(op
tmp_ma->gp_style->flag &= ~GP_MATERIAL_LOCKED;
DEG_id_tag_update(&tmp_ma->id, ID_RECALC_COPY_ON_WRITE);
}
changed = true;
}
}
}
}
/* updates */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
/* copy on write tag is needed, or else no refresh happens */
DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
if (changed) {
/* updates */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
/* copy on write tag is needed, or else no refresh happens */
DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
}
return OPERATOR_FINISHED;
}
@ -3476,7 +3489,6 @@ static int gpencil_set_active_material_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
bool changed = false;
/* Sanity checks. */
if (gpd == NULL) {
@ -3484,6 +3496,7 @@ static int gpencil_set_active_material_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
bool changed = false;
/* Loop all selected strokes. */
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
if (gps->flag & GP_STROKE_SELECT) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,214 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2008, Blender Foundation
* This is a new part of Blender
* Operators for editing Grease Pencil strokes
*/
/** \file
* \ingroup edgpencil
*/
#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "DNA_gpencil_types.h"
#include "DNA_view3d_types.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_gpencil.h"
#include "DEG_depsgraph.h"
#include "gpencil_intern.h"
/* Poll callback for checking if there is an active layer and we are in curve edit mode. */
static bool gpencil_curve_edit_mode_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
return false;
}
bGPdata *gpd = (bGPdata *)ob->data;
if (!GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
return false;
}
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
return (gpl != NULL);
}
static int gpencil_stroke_enter_editcurve_mode_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ob->data;
float error_threshold = RNA_float_get(op->ptr, "error_threshold");
gpd->curve_edit_threshold = error_threshold;
if (ELEM(NULL, gpd)) {
return OPERATOR_CANCELLED;
}
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
if (gpf == gpl->actframe) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* only allow selected and non-converted strokes to be transformed */
if ((gps->flag & GP_STROKE_SELECT && gps->editcurve == NULL) ||
(gps->editcurve != NULL && gps->editcurve->flag & GP_CURVE_NEEDS_STROKE_UPDATE)) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
/* Update the selection from the stroke to the curve. */
BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
}
}
gpd->flag |= GP_DATA_CURVE_EDIT_MODE;
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_enter_editcurve_mode(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Enter curve edit mode";
ot->idname = "GPENCIL_OT_stroke_enter_editcurve_mode";
ot->description = "Called to transform a stroke into a curve";
/* api callbacks */
ot->exec = gpencil_stroke_enter_editcurve_mode_exec;
ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_float(ot->srna,
"error_threshold",
0.1f,
FLT_MIN,
100.0f,
"Error Threshold",
"Threshold on the maximum deviation from the actual stroke",
FLT_MIN,
10.f);
RNA_def_property_ui_range(prop, FLT_MIN, 10.0f, 0.1f, 5);
}
static int gpencil_editcurve_set_handle_type_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ob->data;
const int handle_type = RNA_enum_get(op->ptr, "type");
if (ELEM(NULL, gpd)) {
return OPERATOR_CANCELLED;
}
GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
{
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
BezTriple *bezt = &gpc_pt->bezt;
if (bezt->f2 & SELECT) {
bezt->h1 = handle_type;
bezt->h2 = handle_type;
}
else {
if (bezt->f1 & SELECT) {
bezt->h1 = handle_type;
}
if (bezt->f3 & SELECT) {
bezt->h2 = handle_type;
}
}
}
}
BKE_gpencil_editcurve_recalculate_handles(gps);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
GP_EDITABLE_CURVES_END(gps_iter);
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_editcurve_set_handle_type(wmOperatorType *ot)
{
static const EnumPropertyItem editcurve_handle_type_items[] = {
{HD_FREE, "FREE", 0, "Free", ""},
{HD_AUTO, "AUTOMATIC", 0, "Automatic", ""},
{HD_VECT, "VECTOR", 0, "Vector", ""},
{HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
{0, NULL, 0, NULL, NULL},
};
/* identifiers */
ot->name = "Set handle type";
ot->idname = "GPENCIL_OT_stroke_editcurve_set_handle_type";
ot->description = "Set the type of a edit curve handle";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = gpencil_editcurve_set_handle_type_exec;
ot->poll = gpencil_curve_edit_mode_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
}
/** \} */

View File

@ -1299,11 +1299,11 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
/* simplify stroke */
for (int b = 0; b < tgpf->fill_simplylvl; b++) {
BKE_gpencil_stroke_simplify_fixed(gps);
BKE_gpencil_stroke_simplify_fixed(tgpf->gpd, gps);
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(tgpf->gpd, gps);
}
/* ----------------------- */

View File

@ -343,7 +343,8 @@ struct GHash *gpencil_copybuf_validate_colormap(struct bContext *C);
/* Stroke Editing ------------------------------------ */
void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
void gpencil_stroke_delete_tagged_points(bGPdata *gpd,
bGPDframe *gpf,
bGPDstroke *gps,
bGPDstroke *next_stroke,
int tag_flags,
@ -351,7 +352,7 @@ void gpencil_stroke_delete_tagged_points(bGPDframe *gpf,
int limit);
int gpencil_delete_selected_point_wrap(bContext *C);
void gpencil_subdivide_stroke(bGPDstroke *gps, const int subdivide);
void gpencil_subdivide_stroke(bGPdata *gpd, bGPDstroke *gps, const int subdivide);
/* Layers Enums -------------------------------------- */
@ -447,6 +448,11 @@ void GPENCIL_OT_snap_cursor_to_selected(struct wmOperatorType *ot);
void GPENCIL_OT_reproject(struct wmOperatorType *ot);
void GPENCIL_OT_recalc_geometry(struct wmOperatorType *ot);
/* stroke editcurve */
void GPENCIL_OT_stroke_enter_editcurve_mode(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_editcurve_set_handle_type(struct wmOperatorType *ot);
/* stroke sculpting -- */
void GPENCIL_OT_sculpt_paint(struct wmOperatorType *ot);
@ -692,6 +698,55 @@ struct GP_EditableStrokes_Iter {
} \
(void)0
/**
* Iterate over all editable editcurves in the current context,
* stopping on each usable layer + stroke + curve pair (i.e. gpl, gps and gpc)
* to perform some operations on the curve.
*
* \param gpl: The identifier to use for the layer of the stroke being processed.
* Choose a suitable value to avoid name clashes.
* \param gps: The identifier to use for current stroke being processed.
* Choose a suitable value to avoid name clashes.
* \param gpc: The identifier to use for current editcurve being processed.
* Choose a suitable value to avoid name clashes.
*/
#define GP_EDITABLE_CURVES_BEGIN(gpstroke_iter, C, gpl, gps, gpc) \
{ \
struct GP_EditableStrokes_Iter gpstroke_iter = {{{0}}}; \
Depsgraph *depsgraph_ = CTX_data_ensure_evaluated_depsgraph(C); \
Object *obact_ = CTX_data_active_object(C); \
bGPdata *gpd_ = CTX_data_gpencil_data(C); \
const bool is_multiedit_ = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_); \
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { \
bGPDframe *init_gpf_ = (is_multiedit_) ? gpl->frames.first : gpl->actframe; \
for (bGPDframe *gpf_ = init_gpf_; gpf_; gpf_ = gpf_->next) { \
if ((gpf_ == gpl->actframe) || ((gpf_->flag & GP_FRAME_SELECT) && is_multiedit_)) { \
BKE_gpencil_parent_matrix_get(depsgraph_, obact_, gpl, gpstroke_iter.diff_mat); \
invert_m4_m4(gpstroke_iter.inverse_diff_mat, gpstroke_iter.diff_mat); \
/* loop over strokes */ \
bGPDstroke *gpsn_; \
for (bGPDstroke *gps = gpf_->strokes.first; gps; gps = gpsn_) { \
gpsn_ = gps->next; \
/* skip strokes that are invalid for current view */ \
if (ED_gpencil_stroke_can_use(C, gps) == false) \
continue; \
if (gps->editcurve == NULL) \
continue; \
bGPDcurve *gpc = gps->editcurve; \
/* ... Do Stuff With Strokes ... */
#define GP_EDITABLE_CURVES_END(gpstroke_iter) \
} \
} \
if (!is_multiedit_) { \
break; \
} \
} \
} \
CTX_DATA_END; \
} \
(void)0
/**
* Iterate over all editable strokes using evaluated data in the current context,
* stopping on each usable layer + stroke pair (i.e. gpl and gps)

View File

@ -184,7 +184,7 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
/* Add temp strokes. */
if (gpf) {
bGPDstroke *gps_eval = BKE_gpencil_stroke_duplicate(new_stroke, true);
bGPDstroke *gps_eval = BKE_gpencil_stroke_duplicate(new_stroke, true, true);
gps_eval->flag |= GP_STROKE_TAG;
BLI_addtail(&gpf->strokes, gps_eval);
}
@ -327,7 +327,7 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
}
/* create new stroke */
new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true);
new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
if (valid) {
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
@ -353,7 +353,7 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(new_stroke);
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
/* add to strokes */
BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
}
@ -608,11 +608,11 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
}
/* make copy of source stroke, then adjust pointer to points too */
gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true);
gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true);
gps_dst->flag &= ~GP_STROKE_TAG;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps_dst);
BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps_dst);
BLI_addtail(&gpf_dst->strokes, gps_dst);
}
@ -1050,7 +1050,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
/* create new stroke */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true);
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
if (gps_from->totpoints > gps_to->totpoints) {
@ -1075,7 +1075,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(new_stroke);
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
/* add to strokes */
BLI_addtail(&interFrame->strokes, new_stroke);

View File

@ -172,6 +172,9 @@ static void gpencil_get_elements_len(bContext *C, int *totstrokes, int *totpoint
static void gpencil_dissolve_points(bContext *C)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ob->data;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
bGPDframe *gpf = gpl->actframe;
if (gpf == NULL) {
@ -179,7 +182,7 @@ static void gpencil_dissolve_points(bContext *C)
}
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
gpencil_stroke_delete_tagged_points(gpd, gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
}
}
CTX_DATA_END;
@ -519,7 +522,7 @@ static int gpencil_stroke_merge_exec(bContext *C, wmOperator *op)
gpencil_dissolve_points(C);
}
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
/* free memory */
MEM_SAFE_FREE(original_array);

View File

@ -69,6 +69,13 @@ static bool gpencil_stroke_editmode_poll(bContext *C)
return (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE));
}
/* Poll callback for stroke curve editing mode */
static bool gpencil_stroke_editmode_curve_poll(bContext *C)
{
bGPdata *gpd = CTX_data_gpencil_data(C);
return (GPENCIL_EDIT_MODE(gpd) && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd));
}
/* Poll callback for stroke painting mode */
static bool gpencil_stroke_paintmode_poll(bContext *C)
{
@ -315,6 +322,15 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
keymap->poll = gpencil_stroke_editmode_poll;
}
/* Stroke Curve Editing Keymap - Only when editmode is enabled and in curve edit mode */
static void ed_keymap_gpencil_curve_editing(wmKeyConfig *keyconf)
{
wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Curve Edit Mode", 0, 0);
/* set poll callback - so that this keymap only gets enabled when curve editmode is enabled */
keymap->poll = gpencil_stroke_editmode_curve_poll;
}
/* keys for draw with a drawing brush (no fill) */
static void ed_keymap_gpencil_painting_draw(wmKeyConfig *keyconf)
{
@ -471,6 +487,7 @@ static void ed_keymap_gpencil_weightpainting_draw(wmKeyConfig *keyconf)
void ED_keymap_gpencil(wmKeyConfig *keyconf)
{
ed_keymap_gpencil_general(keyconf);
ed_keymap_gpencil_curve_editing(keyconf);
ed_keymap_gpencil_editing(keyconf);
ed_keymap_gpencil_painting(keyconf);
ed_keymap_gpencil_painting_draw(keyconf);
@ -568,6 +585,11 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_sculpt_paint);
WM_operatortype_append(GPENCIL_OT_weight_paint);
/* Edit stroke editcurve */
WM_operatortype_append(GPENCIL_OT_stroke_enter_editcurve_mode);
WM_operatortype_append(GPENCIL_OT_stroke_editcurve_set_handle_type);
/* Editing (Buttons) ------------ */
WM_operatortype_append(GPENCIL_OT_annotation_add);

View File

@ -1195,7 +1195,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
/* subdivide and smooth the stroke */
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) {
gpencil_subdivide_stroke(gps, subdivide);
gpencil_subdivide_stroke(gpd, gps, subdivide);
}
/* Smooth stroke after subdiv - only if there's something to do for each iteration,
@ -1226,7 +1226,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
/* Simplify adaptive */
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
(brush->gpencil_settings->simplify_f > 0.0f)) {
BKE_gpencil_stroke_simplify_adaptive(gps, brush->gpencil_settings->simplify_f);
BKE_gpencil_stroke_simplify_adaptive(gpd, gps, brush->gpencil_settings->simplify_f);
}
/* reproject to plane (only in 3d space) */
@ -1279,11 +1279,11 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
/* post process stroke */
if ((p->brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
p->brush->gpencil_settings->flag & GP_BRUSH_TRIM_STROKE) {
BKE_gpencil_stroke_trim(gps);
BKE_gpencil_stroke_trim(gpd, gps);
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gpencil_stroke_added_enable(p);
}
@ -1652,7 +1652,7 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
gpencil_stroke_soft_refine(gps);
}
gpencil_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
gpencil_stroke_delete_tagged_points(p->gpd, gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
}
gpencil_update_cache(p->gpd);
}

View File

@ -1082,7 +1082,7 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
/* Update evaluated data. */
ED_gpencil_sbuffer_update_eval(tgpi->gpd, tgpi->ob_eval);
@ -1323,7 +1323,7 @@ static void gpencil_primitive_interaction_end(bContext *C,
copy_v2_v2(gps->aspect_ratio, brush_settings->aspect_ratio);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps);
}
/* transfer stroke from temporary buffer to the actual frame */

View File

@ -303,7 +303,7 @@ static void gpencil_update_geometry(bGPdata *gpd)
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_TAG) {
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
gps->flag &= ~GP_STROKE_TAG;
}
}
@ -1021,7 +1021,7 @@ static void gpencil_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
/* Make a new stroke */
new_stroke = BKE_gpencil_stroke_duplicate(gps, true);
new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true);
new_stroke->next = new_stroke->prev = NULL;
BLI_addtail(&gpf->strokes, new_stroke);
@ -1574,6 +1574,7 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C,
bool changed = false;
bool redo_geom = false;
Object *ob = gso->object;
bGPdata *gpd = ob->data;
char tool = gso->brush->gpencil_sculpt_tool;
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
@ -1672,7 +1673,7 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C,
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
/* Update active frame now, only if material has fill. */
if (gp_style->flag & GP_MATERIAL_FILL_SHOW) {
BKE_gpencil_stroke_geometry_update(gps_active);
BKE_gpencil_stroke_geometry_update(gpd, gps_active);
}
else {
gpencil_recalc_geometry_tag(gps_active);

File diff suppressed because it is too large Load Diff

View File

@ -352,13 +352,14 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
* long stroke. Here the length is checked and removed if the length is too big. */
float length = BKE_gpencil_stroke_length(gps, true);
if (length <= MAX_LENGTH) {
bGPdata *gpd = ob->data;
if (sample > 0.0f) {
/* Resample stroke. Don't need to call to BKE_gpencil_stroke_geometry_update() because
* the sample function already call that. */
BKE_gpencil_stroke_sample(gps, sample, false);
BKE_gpencil_stroke_sample(gpd, gps, sample, false);
}
else {
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
else {

View File

@ -55,6 +55,7 @@
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_main.h"
#include "BKE_material.h"
@ -1144,7 +1145,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
bGPDstroke *gps_active = gps;
/* if duplicate, deselect all points. */
if (keep_original) {
gps_active = BKE_gpencil_stroke_duplicate(gps, true);
gps_active = BKE_gpencil_stroke_duplicate(gps, true, true);
gps_active->flag &= ~GP_STROKE_SELECT;
for (i = 0, pt = gps_active->points; i < gps_active->totpoints; i++, pt++) {
pt->flag &= ~GP_SPOINT_SELECT;
@ -1320,10 +1321,11 @@ void ED_gpencil_project_point_to_plane(const Scene *scene,
/**
* Subdivide a stroke once, by adding a point half way between each pair of existing points
* \param gpd: Datablock
* \param gps: Stroke data
* \param subdivide: Number of times to subdivide
*/
void gpencil_subdivide_stroke(bGPDstroke *gps, const int subdivide)
void gpencil_subdivide_stroke(bGPdata *gpd, bGPDstroke *gps, const int subdivide)
{
bGPDspoint *temp_points;
int totnewpoints, oldtotpoints;
@ -1413,7 +1415,7 @@ void gpencil_subdivide_stroke(bGPDstroke *gps, const int subdivide)
MEM_SAFE_FREE(temp_points);
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
/* Reset parent matrix for all layers. */
@ -2243,8 +2245,12 @@ static void gpencil_copy_points(
}
}
static void gpencil_insert_point(
bGPDstroke *gps, bGPDspoint *a_pt, bGPDspoint *b_pt, const float co_a[3], const float co_b[3])
static void gpencil_insert_point(bGPdata *gpd,
bGPDstroke *gps,
bGPDspoint *a_pt,
bGPDspoint *b_pt,
const float co_a[3],
float co_b[3])
{
bGPDspoint *temp_points;
int totnewpoints, oldtotpoints;
@ -2303,8 +2309,8 @@ static void gpencil_insert_point(
i2++;
}
/* Calculate geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, gps);
MEM_SAFE_FREE(temp_points);
}
@ -2328,7 +2334,8 @@ static float gpencil_calc_factor(const float p2d_a1[2],
}
/* extend selection to stroke intersections */
int ED_gpencil_select_stroke_segment(bGPDlayer *gpl,
int ED_gpencil_select_stroke_segment(bGPdata *gpd,
bGPDlayer *gpl,
bGPDstroke *gps,
bGPDspoint *pt,
bool select,
@ -2483,7 +2490,7 @@ int ED_gpencil_select_stroke_segment(bGPDlayer *gpl,
/* insert new point in the collision points */
if (insert) {
gpencil_insert_point(gps, hit_pointa, hit_pointb, r_hita, r_hitb);
gpencil_insert_point(gpd, gps, hit_pointa, hit_pointb, r_hita, r_hitb);
}
/* free memory */
@ -2611,6 +2618,82 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
}
}
void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
{
/* if toggle, check if we need to select or deselect */
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
{
if (gpc->flag & GP_CURVE_SELECT) {
action = SEL_DESELECT;
}
}
GP_EDITABLE_CURVES_END(gps_iter);
}
if (action == SEL_DESELECT) {
GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
{
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
BEZT_DESEL_ALL(bezt);
}
gpc->flag &= ~GP_CURVE_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
}
GP_EDITABLE_CURVES_END(gps_iter);
}
else {
GP_EDITABLE_STROKES_BEGIN(gps_iter, C, gpl, gps){
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ob->data;
bool selected = false;
/* Make sure stroke has an editcurve */
if (gps->editcurve == NULL) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
bGPDcurve *gpc = gps->editcurve;
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
switch (action) {
case SEL_SELECT:
gpc_pt->flag |= GP_CURVE_POINT_SELECT;
BEZT_SEL_ALL(bezt);
break;
case SEL_INVERT:
gpc_pt->flag ^= GP_CURVE_POINT_SELECT;
BEZT_SEL_INVERT(bezt);
break;
default:
break;
}
if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
selected = true;
}
}
if (selected) {
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
}
else {
gpc->flag &= ~GP_CURVE_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
}
}
GP_EDITABLE_STROKES_END(gps_iter);
}
}
/**
* Ensure the #tGPspoint buffer (while drawing stroke)
* size is enough to save all points of the stroke.

View File

@ -273,7 +273,7 @@ static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op)
changed = true;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
i++;
}
}
@ -291,7 +291,7 @@ static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op)
gps->uv_rotation = opdata->array_rot[i] - uv_rotation;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
i++;
}
}
@ -316,7 +316,7 @@ static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op)
if (gps->flag & GP_STROKE_SELECT) {
gps->uv_scale = opdata->array_scale[i] + scale;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
i++;
}
}
@ -512,7 +512,7 @@ static int gpencil_reset_transform_fill_exec(bContext *C, wmOperator *op)
gps->uv_scale = 1.0f;
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
changed = true;
}
}

View File

@ -819,8 +819,9 @@ static void gpencil_save_selected_point(tGP_BrushVertexpaintData *gso,
gso->pbuffer_used++;
}
/* Select points in this stroke and add to an array to be used later. */
static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
/* Select points in this stroke and add to an array to be used later.
* Returns true if any point was hit and got saved */
static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
bGPDstroke *gps,
const char tool,
const float diff_mat[4][4])
@ -841,9 +842,11 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
int index;
bool include_last = false;
bool saved = false;
/* Check if the stroke collide with brush. */
if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, diff_mat)) {
return;
return false;
}
if (gps->totpoints == 1) {
@ -862,6 +865,7 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
/* apply operation to this point */
if (pt_active != NULL) {
gpencil_save_selected_point(gso, gps_active, 0, pc1);
saved = true;
}
}
}
@ -913,6 +917,7 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
}
hit = true;
gpencil_save_selected_point(gso, gps_active, index, pc1);
saved = true;
}
/* Only do the second point if this is the last segment,
@ -931,6 +936,7 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
hit = true;
gpencil_save_selected_point(gso, gps_active, index, pc2);
include_last = false;
saved = true;
}
}
else {
@ -949,8 +955,8 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
if (pt_active != NULL) {
hit = true;
gpencil_save_selected_point(gso, gps_active, index, pc1);
include_last = false;
saved = true;
}
}
}
@ -970,10 +976,13 @@ static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
for (int repeat = 0; repeat < 50; repeat++) {
gpencil_save_selected_point(gso, gps_active, -1, NULL);
}
saved = true;
}
}
}
}
return saved;
}
/* Apply vertex paint brushes to strokes in the given frame. */
@ -1008,7 +1017,13 @@ static bool gpencil_vertexpaint_brush_do_frame(bContext *C,
}
/* Check points below the brush. */
gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat);
bool hit = gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat);
/* If stroke was hit and has an editcurve the curve needs an update. */
bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
if (gps_active->editcurve != NULL && hit) {
gps_active->editcurve->flag |= GP_CURVE_NEEDS_STROKE_UPDATE;
}
}
/* For Average tool, need calculate the average resulting color from all colors

View File

@ -314,7 +314,8 @@ void ED_gpencil_update_color_uv(struct Main *bmain, struct Material *mat);
* 2 - Hit in point B
* 3 - Hit in point A and B
*/
int ED_gpencil_select_stroke_segment(struct bGPDlayer *gpl,
int ED_gpencil_select_stroke_segment(struct bGPdata *gpd,
struct bGPDlayer *gpl,
struct bGPDstroke *gps,
struct bGPDspoint *pt,
bool select,
@ -324,6 +325,7 @@ int ED_gpencil_select_stroke_segment(struct bGPDlayer *gpl,
float r_hitb[3]);
void ED_gpencil_select_toggle_all(struct bContext *C, int action);
void ED_gpencil_select_curve_toggle_all(struct bContext *C, int action);
/* Ensure stroke sbuffer size is enough */
struct tGPspoint *ED_gpencil_sbuffer_ensure(struct tGPspoint *buffer_array,

View File

@ -1738,6 +1738,10 @@ static void ed_default_handlers(
wmKeyMap *keymap_general = WM_keymap_ensure(wm->defaultconf, "Grease Pencil", 0, 0);
WM_event_add_keymap_handler(handlers, keymap_general);
wmKeyMap *keymap_curve_edit = WM_keymap_ensure(
wm->defaultconf, "Grease Pencil Stroke Curve Edit Mode", 0, 0);
WM_event_add_keymap_handler(handlers, keymap_curve_edit);
wmKeyMap *keymap_edit = WM_keymap_ensure(
wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
WM_event_add_keymap_handler(handlers, keymap_edit);

View File

@ -31,7 +31,9 @@
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "ED_gpencil.h"
@ -63,33 +65,351 @@ static void createTransGPencil_center_get(bGPDstroke *gps, float r_center[3])
}
}
void createTransGPencil(bContext *C, TransInfo *t)
static short get_bezt_sel_triple_flag(BezTriple *bezt, const bool handles_visible)
{
if (t->data_container_len == 0) {
#define SEL_F1 (1 << 0)
#define SEL_F2 (1 << 1)
#define SEL_F3 (1 << 2)
#define SEL_ALL ((1 << 0) | (1 << 1) | (1 << 2))
short flag = 0;
if (handles_visible) {
flag = ((bezt->f1 & SELECT) ? SEL_F1 : 0) | ((bezt->f2 & SELECT) ? SEL_F2 : 0) |
((bezt->f3 & SELECT) ? SEL_F3 : 0);
}
else {
if (bezt->f2 & SELECT) {
flag = SEL_ALL;
}
}
/* Special case for auto & aligned handles */
if (flag != SEL_ALL && flag & SEL_F2) {
if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) {
flag = SEL_ALL;
}
}
#undef SEL_F1
#undef SEL_F2
#undef SEL_F3
return flag;
}
static void createTransGPencil_curves(bContext *C,
TransInfo *t,
Depsgraph *depsgraph,
ToolSettings *ts,
Object *obact,
bGPdata *gpd,
const int cfra_scene,
const bool is_multiedit,
const bool use_multiframe_falloff,
const bool is_prop_edit,
const bool is_prop_edit_connected,
const bool is_scale_thickness)
{
#define SEL_F1 (1 << 0)
#define SEL_F2 (1 << 1)
#define SEL_F3 (1 << 2)
View3D *v3d = t->view;
const bool handle_only_selected_visible = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
const bool handle_all_visible = (v3d->overlay.handle_display == CURVE_HANDLE_ALL);
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
tc->data_len = 0;
/* Number of selected curve points */
uint32_t tot_curve_points = 0, tot_sel_curve_points = 0, tot_points = 0, tot_sel_points = 0;
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* Only editable and visible layers are considered. */
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
/* Check if the color is editable. */
if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) {
continue;
}
/* Check if stroke has an editcurve */
if (gps->editcurve == NULL) {
continue;
}
bGPDcurve *gpc = gps->editcurve;
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
if (bezt->hide) {
continue;
}
const bool handles_visible = (handle_all_visible ||
(handle_only_selected_visible &&
(gpc_pt->flag & GP_CURVE_POINT_SELECT)));
const short sel_flag = get_bezt_sel_triple_flag(bezt, handles_visible);
if (sel_flag & (SEL_F1 | SEL_F2 | SEL_F3)) {
if (sel_flag & SEL_F1) {
tot_sel_points++;
}
if (sel_flag & SEL_F2) {
tot_sel_points++;
}
if (sel_flag & SEL_F3) {
tot_sel_points++;
}
tot_sel_curve_points++;
}
if (is_prop_edit) {
tot_points += 3;
tot_curve_points++;
}
}
}
}
/* If not multiedit out of loop. */
if (!is_multiedit) {
break;
}
}
}
}
if (((is_prop_edit && !is_prop_edit_connected) ? tot_curve_points : tot_sel_points) == 0) {
tc->data_len = 0;
return;
}
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
ToolSettings *ts = CTX_data_tool_settings(C);
int data_len_pt = 0;
bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0;
if (is_prop_edit) {
tc->data_len = tot_points;
data_len_pt = tot_curve_points;
}
else {
tc->data_len = tot_sel_points;
data_len_pt = tot_sel_curve_points;
}
Object *obact = CTX_data_active_object(C);
if (tc->data_len == 0) {
return;
}
transform_around_single_fallback_ex(t, data_len_pt);
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), __func__);
TransData *td = tc->data;
const bool use_around_origins_for_handles_test = ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
transform_mode_use_local_origins(t));
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* Only editable and visible layers are considered. */
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene;
bGPDframe *gpf = gpl->actframe;
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
float diff_mat[4][4], mtx[3][3];
float smtx[3][3];
/* Init multiframe falloff options. */
int f_init = 0;
int f_end = 0;
if (use_multiframe_falloff) {
BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
}
if ((gpf->framenum != cfra) && (!is_multiedit)) {
gpf = BKE_gpencil_frame_addcopy(gpl, cfra);
/* in some weird situations (framelock enabled) return NULL */
if (gpf == NULL) {
continue;
}
if (!is_multiedit) {
init_gpf = gpf;
}
}
/* Calculate difference matrix. */
BKE_gpencil_parent_matrix_get(depsgraph, obact, gpl, diff_mat);
copy_m3_m4(mtx, diff_mat);
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
for (gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
/* If multi-frame and falloff, recalculate and save value. */
float falloff = 1.0f; /* by default no falloff */
if ((is_multiedit) && (use_multiframe_falloff)) {
/* Falloff depends on distance to active frame
* (relative to the overall frame range). */
falloff = BKE_gpencil_multiframe_falloff_calc(
gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
}
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
/* Check if the color is editable. */
if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) {
continue;
}
/* Check if stroke has an editcurve */
if (gps->editcurve == NULL) {
continue;
}
TransData *head, *tail;
head = tail = td;
gps->runtime.multi_frame_falloff = falloff;
bool need_handle_recalc = false;
bGPDcurve *gpc = gps->editcurve;
const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
if (bezt->hide) {
continue;
}
TransDataCurveHandleFlags *hdata = NULL;
bool bezt_use = false;
const bool handles_visible = (handle_all_visible ||
(handle_only_selected_visible &&
(gpc_pt->flag & GP_CURVE_POINT_SELECT)));
const short sel_flag = get_bezt_sel_triple_flag(bezt, handles_visible);
/* Iterate over bezier triple */
for (int j = 0; j < 3; j++) {
bool is_ctrl_point = (j == 1);
bool sel = sel_flag & (1 << j);
if (is_prop_edit || sel) {
copy_v3_v3(td->iloc, bezt->vec[j]);
td->loc = bezt->vec[j];
bool rotate_around_ctrl = !handles_visible ||
(t->around == V3D_AROUND_LOCAL_ORIGINS) ||
(bezt->f2 & SELECT);
copy_v3_v3(td->center, bezt->vec[rotate_around_ctrl ? 1 : j]);
if (!handles_visible || is_ctrl_point) {
if (bezt->f2 & SELECT) {
td->flag = TD_SELECTED;
}
else {
td->flag = 0;
}
}
else if (handles_visible) {
if (BEZT_ISSEL_IDX(bezt, j)) {
td->flag = TD_SELECTED;
}
else {
td->flag = 0;
}
}
td->ext = NULL;
if (is_ctrl_point) {
if (t->mode != TFM_MIRROR) {
if (t->mode != TFM_GPENCIL_OPACITY) {
if (is_scale_thickness) {
td->val = &(gpc_pt->pressure);
td->ival = gpc_pt->pressure;
}
}
else {
td->val = &(gpc_pt->strength);
td->ival = gpc_pt->strength;
}
}
}
else {
td->val = NULL;
}
if (hdata == NULL) {
if (is_ctrl_point && ((sel_flag & SEL_F1 & SEL_F3) == 0)) {
hdata = initTransDataCurveHandles(td, bezt);
}
else if (!is_ctrl_point) {
hdata = initTransDataCurveHandles(td, bezt);
}
}
td->extra = gps;
td->ob = obact;
copy_m3_m3(td->smtx, smtx);
copy_m3_m3(td->mtx, mtx);
copy_m3_m3(td->axismtx, mtx);
td++;
tail++;
}
bezt_use |= sel;
}
/* Update the handle types so transformation is possible */
if (bezt_use && !ELEM(t->mode, TFM_GPENCIL_OPACITY, TFM_GPENCIL_SHRINKFATTEN)) {
BKE_nurb_bezt_handle_test(
bezt, SELECT, handles_visible, use_around_origins_for_handles_test);
need_handle_recalc = true;
}
}
if (is_prop_edit && (head != tail)) {
calc_distanceCurveVerts(head, tail - 1, is_cyclic);
}
if (need_handle_recalc) {
BKE_gpencil_editcurve_recalculate_handles(gps);
}
}
}
/* If not multiedit out of loop. */
if (!is_multiedit) {
break;
}
}
}
}
#undef SEL_F1
#undef SEL_F2
#undef SEL_F3
}
static void createTransGPencil_strokes(bContext *C,
TransInfo *t,
Depsgraph *depsgraph,
ToolSettings *ts,
Object *obact,
bGPdata *gpd,
const int cfra_scene,
const bool is_multiedit,
const bool use_multiframe_falloff,
const bool is_prop_edit,
const bool is_prop_edit_connected,
const bool is_scale_thickness)
{
TransData *td = NULL;
float mtx[3][3], smtx[3][3];
const Scene *scene = CTX_data_scene(C);
const int cfra_scene = CFRA;
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0;
const bool is_scale_thickness = ((t->mode == TFM_GPENCIL_SHRINKFATTEN) ||
(ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SCALE_THICKNESS));
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* == Grease Pencil Strokes to Transform Data ==
* Grease Pencil stroke points can be a mixture of 2D (screen-space),
* or 3D coordinates. However, they're always saved as 3D points.
@ -98,15 +418,6 @@ void createTransGPencil(bContext *C, TransInfo *t)
*/
tc->data_len = 0;
if (gpd == NULL) {
return;
}
/* initialize falloff curve */
if (is_multiedit) {
BKE_curvemapping_init(ts->gp_sculpt.cur_falloff);
}
/* First Pass: Count the number of data-points required for the strokes,
* (and additional info about the configuration - e.g. 2D/3D?).
*/
@ -368,6 +679,71 @@ void createTransGPencil(bContext *C, TransInfo *t)
}
}
void createTransGPencil(bContext *C, TransInfo *t)
{
if (t->data_container_len == 0) {
return;
}
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
const Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
Object *obact = CTX_data_active_object(C);
bGPdata *gpd = obact->data;
BLI_assert(gpd != NULL);
const int cfra_scene = CFRA;
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) !=
0;
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0;
const bool is_scale_thickness = ((t->mode == TFM_GPENCIL_SHRINKFATTEN) ||
(ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SCALE_THICKNESS));
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* initialize falloff curve */
if (is_multiedit) {
BKE_curvemapping_init(ts->gp_sculpt.cur_falloff);
}
if (gpd == NULL) {
return;
}
if (is_curve_edit) {
createTransGPencil_curves(C,
t,
depsgraph,
ts,
obact,
gpd,
cfra_scene,
is_multiedit,
use_multiframe_falloff,
is_prop_edit,
is_prop_edit_connected,
is_scale_thickness);
}
else {
createTransGPencil_strokes(C,
t,
depsgraph,
ts,
obact,
gpd,
cfra_scene,
is_multiedit,
use_multiframe_falloff,
is_prop_edit,
is_prop_edit_connected,
is_scale_thickness);
}
}
/* force recalculation of triangles during transformation */
void recalcData_gpencil_strokes(TransInfo *t)
{
@ -375,13 +751,19 @@ void recalcData_gpencil_strokes(TransInfo *t)
GHash *strokes = BLI_ghash_ptr_new(__func__);
TransData *td = tc->data;
bGPdata *gpd = td->ob->data;
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
for (int i = 0; i < tc->data_len; i++, td++) {
bGPDstroke *gps = td->extra;
if ((gps != NULL) && (!BLI_ghash_haskey(strokes, gps))) {
BLI_ghash_insert(strokes, gps, gps);
if (is_curve_edit && gps->editcurve != NULL) {
BKE_gpencil_editcurve_recalculate_handles(gps);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
BLI_ghash_free(strokes, NULL, NULL);

View File

@ -799,7 +799,7 @@ void postTrans(bContext *C, TransInfo *t)
if (t->data_len_all != 0) {
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
/* free data malloced per trans-data */
if (ELEM(t->obedit_type, OB_CURVE, OB_SURF) || (t->spacetype == SPACE_GRAPH)) {
if (ELEM(t->obedit_type, OB_CURVE, OB_SURF, OB_GPENCIL) || (t->spacetype == SPACE_GRAPH)) {
TransData *td = tc->data;
for (int a = 0; a < tc->data_len; a++, td++) {
if (td->flag & TD_BEZTRIPLE) {

View File

@ -653,6 +653,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
Object *ob = OBACT(view_layer);
bGPdata *gpd = CTX_data_gpencil_data(C);
const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
const bool is_curve_edit = GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
int a, totsel = 0;
const int pivot_point = scene->toolsettings->transform_pivot_point;
@ -711,16 +712,39 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
continue;
}
/* we're only interested in selected points here... */
if (gps->flag & GP_STROKE_SELECT) {
bGPDspoint *pt;
int i;
if (is_curve_edit) {
if (gps->editcurve == NULL) {
continue;
}
/* Change selection status of all points, then make the stroke match */
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
calc_tw_center_with_matrix(tbounds, &pt->x, use_mat_local, diff_mat);
totsel++;
bGPDcurve *gpc = gps->editcurve;
if (gpc->flag & GP_CURVE_SELECT) {
for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
for (uint32_t j = 0; j < 3; j++) {
if (BEZT_ISSEL_IDX(bezt, j)) {
calc_tw_center_with_matrix(tbounds, bezt->vec[j], use_mat_local, diff_mat);
totsel++;
}
}
}
}
}
}
else {
/* we're only interested in selected points here... */
if (gps->flag & GP_STROKE_SELECT) {
bGPDspoint *pt;
int i;
/* Change selection status of all points, then make the stroke match */
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
calc_tw_center_with_matrix(tbounds, &pt->x, use_mat_local, diff_mat);
totsel++;
}
}
}
}

View File

@ -29,6 +29,8 @@
#include "BKE_context.h"
#include "BKE_unit.h"
#include "DNA_gpencil_types.h"
#include "ED_screen.h"
#include "UI_interface.h"
@ -68,8 +70,16 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
BLI_snprintf(str, sizeof(str), TIP_("Opacity: %3f"), ratio);
}
bool recalc = false;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
bGPdata *gpd = td->ob->data;
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* Only recalculate data when in curve edit mode. */
if (is_curve_edit) {
recalc = true;
}
for (i = 0; i < tc->data_len; i++, td++) {
if (td->flag & TD_SKIP) {
continue;
@ -84,6 +94,10 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
}
}
if (recalc) {
recalcData(t);
}
ED_area_status_text(t->area, str);
}

View File

@ -29,6 +29,8 @@
#include "BKE_context.h"
#include "BKE_unit.h"
#include "DNA_gpencil_types.h"
#include "ED_screen.h"
#include "UI_interface.h"
@ -68,8 +70,16 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
BLI_snprintf(str, sizeof(str), TIP_("Shrink/Fatten: %3f"), ratio);
}
bool recalc = false;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
bGPdata *gpd = td->ob->data;
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* Only recalculate data when in curve edit mode. */
if (is_curve_edit) {
recalc = true;
}
for (i = 0; i < tc->data_len; i++, td++) {
if (td->flag & TD_SKIP) {
continue;
@ -86,6 +96,10 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
}
}
if (recalc) {
recalcData(t);
}
ED_area_status_text(t->area, str);
}

View File

@ -125,10 +125,11 @@ static void deformStroke(GpencilModifierData *md,
if (!mmd->object) {
return;
}
bGPdata *gpd = ob->data;
gpencil_deform_verts(mmd, ob, gps);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
static void bakeModifier(Main *UNUSED(bmain),

View File

@ -255,7 +255,7 @@ static void generate_geometry(GpencilModifierData *md,
/* Duplicate original strokes to create this instance. */
LISTBASE_FOREACH_BACKWARD (tmpStrokes *, iter, &stroke_cache) {
/* Duplicate stroke */
bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(iter->gps, true);
bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(iter->gps, true, true);
/* Move points */
for (int i = 0; i < iter->gps->totpoints; i++) {

View File

@ -115,7 +115,8 @@ static void gpf_clear_all_strokes(bGPDframe *gpf)
*
* Note: This won't be called if all points are present/removed
*/
static void reduce_stroke_points(bGPDstroke *gps,
static void reduce_stroke_points(bGPdata *gpd,
bGPDstroke *gps,
const int num_points,
const eBuildGpencil_Transition transition)
{
@ -180,7 +181,7 @@ static void reduce_stroke_points(bGPDstroke *gps,
gps->totpoints = num_points;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
/* --------------------------------------------- */
@ -197,7 +198,10 @@ typedef struct tStrokeBuildDetails {
} tStrokeBuildDetails;
/* Sequential - Show strokes one after the other */
static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, float fac)
static void build_sequential(BuildGpencilModifierData *mmd,
bGPdata *gpd,
bGPDframe *gpf,
float fac)
{
const size_t tot_strokes = BLI_listbase_count(&gpf->strokes);
bGPDstroke *gps;
@ -236,25 +240,25 @@ static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa
size_t last_visible = 0;
switch (mmd->transition) {
/* Show in forward order
* - As fac increases, the number of visible points increases
*/
/* Show in forward order
* - As fac increases, the number of visible points increases
*/
case GP_BUILD_TRANSITION_GROW:
first_visible = 0; /* always visible */
last_visible = (size_t)roundf(totpoints * fac);
break;
/* Hide in reverse order
* - As fac increases, the number of points visible at the end decreases
*/
/* Hide in reverse order
* - As fac increases, the number of points visible at the end decreases
*/
case GP_BUILD_TRANSITION_SHRINK:
first_visible = 0; /* always visible (until last point removed) */
last_visible = (size_t)(totpoints * (1.0f - fac));
break;
/* Hide in forward order
* - As fac increases, the early points start getting hidden
*/
/* Hide in forward order
* - As fac increases, the early points start getting hidden
*/
case GP_BUILD_TRANSITION_FADE:
first_visible = (size_t)(totpoints * fac);
last_visible = totpoints; /* i.e. visible until the end, unless first overlaps this */
@ -278,12 +282,12 @@ static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa
else if (first_visible > cell->start_idx) {
/* Starts partway through this stroke */
int num_points = cell->end_idx - first_visible;
reduce_stroke_points(cell->gps, num_points, mmd->transition);
reduce_stroke_points(gpd, cell->gps, num_points, mmd->transition);
}
else {
/* Ends partway through this stroke */
int num_points = last_visible - cell->start_idx;
reduce_stroke_points(cell->gps, num_points, mmd->transition);
reduce_stroke_points(gpd, cell->gps, num_points, mmd->transition);
}
}
}
@ -295,7 +299,10 @@ static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa
/* --------------------------------------------- */
/* Concurrent - Show multiple strokes at once */
static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, float fac)
static void build_concurrent(BuildGpencilModifierData *mmd,
bGPdata *gpd,
bGPDframe *gpf,
float fac)
{
bGPDstroke *gps, *gps_next;
int max_points = 0;
@ -390,16 +397,14 @@ static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa
}
else if (num_points < gps->totpoints) {
/* Remove some points */
reduce_stroke_points(gps, num_points, mmd->transition);
reduce_stroke_points(gpd, gps, num_points, mmd->transition);
}
}
}
/* --------------------------------------------- */
static void generate_geometry(GpencilModifierData *md,
Depsgraph *depsgraph,
bGPDlayer *gpl,
bGPDframe *gpf)
static void generate_geometry(
GpencilModifierData *md, Depsgraph *depsgraph, bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf)
{
BuildGpencilModifierData *mmd = (BuildGpencilModifierData *)md;
const bool reverse = (mmd->transition != GP_BUILD_TRANSITION_GROW);
@ -504,11 +509,11 @@ static void generate_geometry(GpencilModifierData *md,
/* Time management mode */
switch (mmd->mode) {
case GP_BUILD_MODE_SEQUENTIAL:
build_sequential(mmd, gpf, fac);
build_sequential(mmd, gpd, gpf, fac);
break;
case GP_BUILD_MODE_CONCURRENT:
build_concurrent(mmd, gpf, fac);
build_concurrent(mmd, gpd, gpf, fac);
break;
default:
@ -530,7 +535,7 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec
if (gpf == NULL) {
continue;
}
generate_geometry(md, depsgraph, gpl, gpf);
generate_geometry(md, depsgraph, gpd, gpl, gpf);
}
}

View File

@ -228,6 +228,7 @@ static void deformStroke(GpencilModifierData *md,
mmd->flag & GP_HOOK_INVERT_MATERIAL)) {
return;
}
bGPdata *gpd = ob->data;
/* init struct */
tData.curfalloff = mmd->curfalloff;
@ -273,7 +274,7 @@ static void deformStroke(GpencilModifierData *md,
gpencil_hook_co_apply(&tData, weight, pt);
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
/* FIXME: Ideally we be doing this on a copy of the main depsgraph

View File

@ -85,6 +85,7 @@ static void deformStroke(GpencilModifierData *md,
bGPDframe *UNUSED(gpf),
bGPDstroke *gps)
{
bGPdata *gpd = ob->data;
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
@ -121,7 +122,7 @@ static void deformStroke(GpencilModifierData *md,
(struct LatticeDeformData *)mmd->cache_data, &pt->x, mmd->strength * weight);
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
/* FIXME: Ideally we be doing this on a copy of the main depsgraph

View File

@ -151,7 +151,7 @@ static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gp
mmd->flag & GP_MIRROR_INVERT_PASS,
mmd->flag & GP_MIRROR_INVERT_LAYERPASS,
mmd->flag & GP_MIRROR_INVERT_MATERIAL)) {
gps_new = BKE_gpencil_stroke_duplicate(gps, true);
gps_new = BKE_gpencil_stroke_duplicate(gps, true, true);
update_position(ob, mmd, gps_new, xi);
BLI_addtail(&gpf->strokes, gps_new);
}

View File

@ -129,6 +129,7 @@ static void duplicateStroke(Object *ob,
float fading_thickness,
float fading_opacity)
{
bGPdata *gpd = ob->data;
int i;
bGPDstroke *new_gps = NULL;
float stroke_normal[3];
@ -172,7 +173,7 @@ static void duplicateStroke(Object *ob,
* to be processed, since we duplicate its data. */
for (i = count - 1; i >= 0; i--) {
if (i != 0) {
new_gps = BKE_gpencil_stroke_duplicate(gps, true);
new_gps = BKE_gpencil_stroke_duplicate(gps, true, true);
BLI_addtail(results, new_gps);
}
else {
@ -199,7 +200,7 @@ static void duplicateStroke(Object *ob,
}
/* Calc geometry data. */
if (new_gps != NULL) {
BKE_gpencil_stroke_geometry_update(new_gps);
BKE_gpencil_stroke_geometry_update(gpd, new_gps);
}
MEM_freeN(t1_array);
MEM_freeN(t2_array);

View File

@ -100,6 +100,7 @@ static void deformStroke(GpencilModifierData *md,
mmd->flag & GP_OFFSET_INVERT_MATERIAL)) {
return;
}
bGPdata *gpd = ob->data;
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
@ -125,7 +126,7 @@ static void deformStroke(GpencilModifierData *md,
mul_m4_v3(mat, &pt->x);
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
static void bakeModifier(struct Main *UNUSED(bmain),

View File

@ -92,26 +92,26 @@ static void deformStroke(GpencilModifierData *md,
mmd->flag & GP_SIMPLIFY_INVERT_MATERIAL)) {
return;
}
bGPdata *gpd = ob->data;
/* Select simplification mode. */
switch (mmd->mode) {
case GP_SIMPLIFY_FIXED: {
for (int i = 0; i < mmd->step; i++) {
BKE_gpencil_stroke_simplify_fixed(gps);
BKE_gpencil_stroke_simplify_fixed(gpd, gps);
}
break;
}
case GP_SIMPLIFY_ADAPTIVE: {
/* simplify stroke using Ramer-Douglas-Peucker algorithm */
BKE_gpencil_stroke_simplify_adaptive(gps, mmd->factor);
BKE_gpencil_stroke_simplify_adaptive(gpd, gps, mmd->factor);
break;
}
case GP_SIMPLIFY_SAMPLE: {
BKE_gpencil_stroke_sample(gps, mmd->length, false);
BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false);
break;
}
case GP_SIMPLIFY_MERGE: {
BKE_gpencil_stroke_merge_distance(gpf, gps, mmd->distance, true);
BKE_gpencil_stroke_merge_distance(gpd, gpf, gps, mmd->distance, true);
break;
}
default:

View File

@ -80,6 +80,7 @@ static void deformStroke(GpencilModifierData *md,
bGPDstroke *gps)
{
SubdivGpencilModifierData *mmd = (SubdivGpencilModifierData *)md;
bGPdata *gpd = ob->data;
/* It makes sense when adding points to a straight line */
/* e.g. for creating thickness variation in later modifiers. */
@ -100,7 +101,7 @@ static void deformStroke(GpencilModifierData *md,
return;
}
BKE_gpencil_stroke_subdivide(gps, mmd->level, mmd->type);
BKE_gpencil_stroke_subdivide(gpd, gps, mmd->level, mmd->type);
/* If the stroke is cyclic, must generate the closing geometry. */
if (gps->flag & GP_STROKE_CYCLIC) {

View File

@ -82,6 +82,7 @@ static void deformStroke(GpencilModifierData *md,
{
TextureGpencilModifierData *mmd = (TextureGpencilModifierData *)md;
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
bGPdata *gpd = ob->data;
if (!is_stroke_affected_by_modifier(ob,
mmd->layername,
@ -102,7 +103,7 @@ static void deformStroke(GpencilModifierData *md,
gps->uv_translation[0] += mmd->fill_offset[0];
gps->uv_translation[1] += mmd->fill_offset[1];
gps->uv_scale *= mmd->fill_scale;
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
if (ELEM(mmd->mode, STROKE, STROKE_AND_FILL)) {

View File

@ -519,6 +519,10 @@ typedef enum eBezTriple_KeyframeType {
(bezt)->f2 & SELECT : \
BEZT_ISSEL_ANY(bezt))
#define BEZT_ISSEL_IDX(bezt, i) \
((i == 0 && (bezt)->f1 & SELECT) || (i == 1 && (bezt)->f2 & SELECT) || \
(i == 2 && (bezt)->f3 & SELECT))
#define BEZT_SEL_ALL(bezt) \
{ \
(bezt)->f1 |= SELECT; \
@ -533,6 +537,49 @@ typedef enum eBezTriple_KeyframeType {
(bezt)->f3 &= ~SELECT; \
} \
((void)0)
#define BEZT_SEL_INVERT(bezt) \
{ \
(bezt)->f1 ^= SELECT; \
(bezt)->f2 ^= SELECT; \
(bezt)->f3 ^= SELECT; \
} \
((void)0)
#define BEZT_SEL_IDX(bezt, i) \
{ \
switch (i) { \
case 0: \
(bezt)->f1 |= SELECT; \
break; \
case 1: \
(bezt)->f2 |= SELECT; \
break; \
case 2: \
(bezt)->f3 |= SELECT; \
break; \
default: \
break; \
} \
} \
((void)0)
#define BEZT_DESEL_IDX(bezt, i) \
{ \
switch (i) { \
case 0: \
(bezt)->f1 &= ~SELECT; \
break; \
case 1: \
(bezt)->f2 &= ~SELECT; \
break; \
case 2: \
(bezt)->f3 &= ~SELECT; \
break; \
default: \
break; \
} \
} \
((void)0)
#define BEZT_IS_AUTOH(bezt) \
(ELEM((bezt)->h1, HD_AUTO, HD_AUTO_ANIM) && ELEM((bezt)->h2, HD_AUTO, HD_AUTO_ANIM))

View File

@ -29,6 +29,7 @@
struct AnimData;
struct MDeformVert;
struct Curve;
#define GP_DEFAULT_PIX_FACTOR 1.0f
#define GP_DEFAULT_GRID_LINES 4
@ -36,6 +37,10 @@ struct MDeformVert;
#define GP_MATERIAL_BUFFER_LEN 256
#define GP_DEFAULT_CURVE_RESOLUTION 32
#define GP_DEFAULT_CURVE_ERROR 0.1f
#define GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE M_PI_2
/* ***************************************** */
/* GP Stroke Points */
@ -165,6 +170,61 @@ typedef enum eGPDpalette_Flag {
PL_PALETTE_ACTIVE = (1 << 0),
} eGPDpalette_Flag;
/* ***************************************** */
/* GP Curve Point */
typedef struct bGPDcurve_point {
/** Bezier Triple for the handles and control points. */
BezTriple bezt;
/** Pressure of input device (from 0 to 1) at this point. */
float pressure;
/** Color strength (used for alpha factor). */
float strength;
/** Index of corresponding point in gps->points. */
int point_index;
/** Additional options. */
int flag;
/** Factor of uv along the stroke. */
float uv_fac;
/** Uv rotation for dot mode. */
float uv_rot;
/** Uv for fill mode. */
float uv_fill[2];
/** Vertex Color RGBA (A=mix factor). */
float vert_color[4];
char _pad[4];
} bGPDcurve_point;
/* bGPDcurve_point->flag */
typedef enum eGPDcurve_point_Flag {
GP_CURVE_POINT_SELECT = (1 << 0),
} eGPDcurve_point_Flag;
/* ***************************************** */
/* GP Curve */
/* Curve for Bezier Editing. */
typedef struct bGPDcurve {
/** Array of BezTriple. */
bGPDcurve_point *curve_points;
/** Total number of curve points. */
int tot_curve_points;
/** General flag. */
short flag;
char _pad[2];
} bGPDcurve;
/* bGPDcurve_Flag->flag */
typedef enum bGPDcurve_Flag {
/* Flag to indicated that the stroke data has been changed and the curve needs to be refitted */
GP_CURVE_NEEDS_STROKE_UPDATE = (1 << 0),
/* Curve is selected */
GP_CURVE_SELECT = (1 << 1),
} bGPDcurve_Flag;
/* ***************************************** */
/* GP Strokes */
@ -180,7 +240,8 @@ typedef struct bGPDstroke_Runtime {
int stroke_start;
/** Triangle offset in the ibo where this fill starts. */
int fill_start;
int _pad[1];
/** Curve Handles offset in the ibo where this handle starts. */
int curve_start;
/** Original stroke (used to dereference evaluated data) */
struct bGPDstroke *gps_orig;
@ -245,6 +306,9 @@ typedef struct bGPDstroke {
/** Vertex Color for Fill (one for all stroke, A=mix factor). */
float vert_color_fill[4];
/** Curve used to edit the stroke using Bezier handlers. */
struct bGPDcurve *editcurve;
bGPDstroke_Runtime runtime;
} bGPDstroke;
@ -263,6 +327,9 @@ typedef enum eGPDstroke_Flag {
/* Flag used to indicate that stroke is used for fill close and must use
* fill color for stroke and no fill area */
GP_STROKE_NOFILL = (1 << 8),
/* Flag to indicated that the editcurve has been changed and the stroke needs to be updated with
* the curve data */
GP_STROKE_NEEDS_CURVE_UPDATE = (1 << 9),
/* only for use with stroke-buffer (while drawing arrows) */
GP_STROKE_USE_ARROW_START = (1 << 12),
/* only for use with stroke-buffer (while drawing arrows) */
@ -562,7 +629,12 @@ typedef struct bGPdata {
ListBase layers;
/** Settings for this data-block. */
int flag;
char _pad1[4];
/** Default resolution for generated curves using curve editing method. */
int curve_edit_resolution;
/** Curve Editing error threshold. */
float curve_edit_threshold;
/** Curve Editing corner angle (less or equal is treated as corner). */
float curve_edit_corner_angle;
/* Palettes */
/** List of bGPDpalette's - Deprecated (2.78 - 2.79 only). */
@ -680,6 +752,11 @@ typedef enum eGPdata_Flag {
/* Autolock not active layers */
GP_DATA_AUTOLOCK_LAYERS = (1 << 20),
/* Enable Bezier Editing Curve (a submode of Edit mode). */
GP_DATA_CURVE_EDIT_MODE = (1 << 21),
/* Use adaptive curve resolution */
GP_DATA_CURVE_ADAPTIVE_RESOLUTION = (1 << 22),
} eGPdata_Flag;
/* gpd->onion_flag */
@ -725,6 +802,9 @@ typedef enum eGP_DrawMode {
GP_DATA_STROKE_WEIGHTMODE | GP_DATA_STROKE_VERTEXMODE)) && \
((gpd)->flag & GP_DATA_STROKE_MULTIEDIT))
#define GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd) \
((gpd) && ((gpd)->flag & (GP_DATA_STROKE_EDITMODE)) && ((gpd)->flag & GP_DATA_CURVE_EDIT_MODE))
/* Macros to check grease pencil modes */
#define GPENCIL_ANY_MODE(gpd) \
((gpd) && ((gpd)->flag & \

View File

@ -1090,7 +1090,7 @@ typedef struct GP_Sculpt_Settings {
int lock_axis;
/** Threshold for intersections */
float isect_threshold;
char _pad_[4];
char _pad[4];
/** Multiframe edit falloff effect by frame. */
struct CurveMapping *cur_falloff;
/** Curve used for primitive tools. */

View File

@ -23,6 +23,7 @@
#include "BLI_math.h"
#include "DNA_brush_types.h"
#include "DNA_curve_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@ -160,6 +161,7 @@ static const EnumPropertyItem rna_enum_gpencil_caps_modes_items[] = {
#ifdef RNA_RUNTIME
# include "BLI_ghash.h"
# include "BLI_listbase.h"
# include "BLI_string_utils.h"
# include "WM_api.h"
@ -167,6 +169,7 @@ static const EnumPropertyItem rna_enum_gpencil_caps_modes_items[] = {
# include "BKE_action.h"
# include "BKE_animsys.h"
# include "BKE_gpencil.h"
# include "BKE_gpencil_curve.h"
# include "BKE_gpencil_geom.h"
# include "BKE_icons.h"
@ -179,6 +182,71 @@ static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointe
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
}
static void rna_GPencil_curve_edit_mode_toggle(Main *bmain, Scene *scene, PointerRNA *ptr)
{
ToolSettings *ts = scene->toolsettings;
bGPdata *gpd = (bGPdata *)ptr->owner_id;
/* Curve edit mode is turned on. */
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
/* If the current select mode is segment and the Bezier mode is on, change
* to Point because segment is not supported. */
if (ts->gpencil_selectmode_edit == GP_SELECTMODE_SEGMENT) {
ts->gpencil_selectmode_edit = GP_SELECTMODE_POINT;
}
BKE_gpencil_strokes_selected_update_editcurve(gpd);
}
/* Curve edit mode is turned off. */
else {
BKE_gpencil_strokes_selected_sync_selection_editcurve(gpd);
}
/* Standard update. */
rna_GPencil_update(bmain, scene, ptr);
}
static void rna_GPencil_stroke_curve_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
bGPdata *gpd = (bGPdata *)ptr->owner_id;
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->actframe != NULL) {
bGPDframe *gpf = gpl->actframe;
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->editcurve != NULL) {
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
}
}
rna_GPencil_update(bmain, scene, ptr);
}
static void rna_GPencil_stroke_curve_resolution_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
bGPdata *gpd = (bGPdata *)ptr->owner_id;
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->actframe != NULL) {
bGPDframe *gpf = gpl->actframe;
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->editcurve != NULL) {
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
}
}
rna_GPencil_update(bmain, scene, ptr);
}
static void rna_GPencil_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
DEG_id_tag_update(ptr->owner_id, ID_RECALC_TRANSFORM);
@ -191,11 +259,12 @@ static void rna_GPencil_dependency_update(Main *bmain, Scene *UNUSED(scene), Poi
static void rna_GPencil_uv_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
bGPdata *gpd = (bGPdata *)ptr->owner_id;
/* Force to recalc the UVs. */
bGPDstroke *gps = (bGPDstroke *)ptr->data;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gps);
BKE_gpencil_stroke_geometry_update(gpd, gps);
DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
@ -669,7 +738,7 @@ static void rna_GPencil_stroke_point_add(
stroke->totpoints += count;
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(stroke);
BKE_gpencil_stroke_geometry_update(gpd, stroke);
DEG_id_tag_update(&gpd->id,
ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
@ -730,7 +799,7 @@ static void rna_GPencil_stroke_point_pop(ID *id,
}
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(stroke);
BKE_gpencil_stroke_geometry_update(gpd, stroke);
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
@ -808,6 +877,32 @@ static void rna_GPencil_stroke_select_set(PointerRNA *ptr, const bool value)
}
}
static void rna_GPencil_curve_select_set(PointerRNA *ptr, const bool value)
{
bGPDcurve *gpc = ptr->data;
/* Set new value. */
if (value) {
gpc->flag |= GP_CURVE_SELECT;
}
else {
gpc->flag &= ~GP_CURVE_SELECT;
}
/* Ensure that the curves's points are selected in the same way. */
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
if (value) {
gpc_pt->flag |= GP_CURVE_POINT_SELECT;
BEZT_SEL_ALL(bezt);
}
else {
gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
BEZT_DESEL_ALL(bezt);
}
}
}
static bGPDframe *rna_GPencil_frame_new(bGPDlayer *layer,
ReportList *reports,
int frame_number,
@ -969,6 +1064,100 @@ static char *rna_GreasePencilGrid_path(PointerRNA *UNUSED(ptr))
return BLI_strdup("grid");
}
static void rna_GpencilCurvePoint_BezTriple_handle1_get(PointerRNA *ptr, float *values)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
copy_v3_v3(values, cpt->bezt.vec[0]);
}
static void rna_GpencilCurvePoint_BezTriple_handle1_set(PointerRNA *ptr, const float *values)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
copy_v3_v3(cpt->bezt.vec[0], values);
}
static bool rna_GpencilCurvePoint_BezTriple_handle1_select_get(PointerRNA *ptr)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
return cpt->bezt.f1;
}
static void rna_GpencilCurvePoint_BezTriple_handle1_select_set(PointerRNA *ptr, const bool value)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
cpt->bezt.f1 = value;
}
static void rna_GpencilCurvePoint_BezTriple_handle2_get(PointerRNA *ptr, float *values)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
copy_v3_v3(values, cpt->bezt.vec[2]);
}
static void rna_GpencilCurvePoint_BezTriple_handle2_set(PointerRNA *ptr, const float *values)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
copy_v3_v3(cpt->bezt.vec[2], values);
}
static bool rna_GpencilCurvePoint_BezTriple_handle2_select_get(PointerRNA *ptr)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
return cpt->bezt.f3;
}
static void rna_GpencilCurvePoint_BezTriple_handle2_select_set(PointerRNA *ptr, const bool value)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
cpt->bezt.f3 = value;
}
static void rna_GpencilCurvePoint_BezTriple_ctrlpoint_get(PointerRNA *ptr, float *values)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
copy_v3_v3(values, cpt->bezt.vec[1]);
}
static void rna_GpencilCurvePoint_BezTriple_ctrlpoint_set(PointerRNA *ptr, const float *values)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
copy_v3_v3(cpt->bezt.vec[1], values);
}
static bool rna_GpencilCurvePoint_BezTriple_ctrlpoint_select_get(PointerRNA *ptr)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
return cpt->bezt.f2;
}
static void rna_GpencilCurvePoint_BezTriple_ctrlpoint_select_set(PointerRNA *ptr, const bool value)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
cpt->bezt.f2 = value;
}
static bool rna_GpencilCurvePoint_BezTriple_hide_get(PointerRNA *ptr)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
return (bool)cpt->bezt.hide;
}
static void rna_GpencilCurvePoint_BezTriple_hide_set(PointerRNA *ptr, const bool value)
{
bGPDcurve_point *cpt = (bGPDcurve_point *)ptr->data;
cpt->bezt.hide = value;
}
static bool rna_stroke_has_edit_curve_get(PointerRNA *ptr)
{
bGPDstroke *gps = (bGPDstroke *)ptr->data;
if (gps->editcurve != NULL) {
return true;
}
return false;
}
#else
static void rna_def_gpencil_stroke_point(BlenderRNA *brna)
@ -1106,6 +1295,149 @@ static void rna_def_gpencil_triangle(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
static void rna_def_gpencil_curve_point(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "GPencilEditCurvePoint", NULL);
RNA_def_struct_sdna(srna, "bGPDcurve_point");
RNA_def_struct_ui_text(srna, "Bezier Curve Point", "Bezier curve point with two handles");
/* Boolean values */
prop = RNA_def_property(srna, "select_left_handle", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop,
"rna_GpencilCurvePoint_BezTriple_handle1_select_get",
"rna_GpencilCurvePoint_BezTriple_handle1_select_set");
RNA_def_property_ui_text(prop, "Handle 1 selected", "Handle 1 selection status");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "select_right_handle", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop,
"rna_GpencilCurvePoint_BezTriple_handle2_select_get",
"rna_GpencilCurvePoint_BezTriple_handle2_select_set");
RNA_def_property_ui_text(prop, "Handle 2 selected", "Handle 2 selection status");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "select_control_point", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop,
"rna_GpencilCurvePoint_BezTriple_ctrlpoint_select_get",
"rna_GpencilCurvePoint_BezTriple_ctrlpoint_select_set");
RNA_def_property_ui_text(prop, "Control Point selected", "Control point selection status");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop,
"rna_GpencilCurvePoint_BezTriple_hide_get",
"rna_GpencilCurvePoint_BezTriple_hide_set");
RNA_def_property_ui_text(prop, "Hide", "Visibility status");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
/* Vector values */
prop = RNA_def_property(srna, "handle_left", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_array(prop, 3);
RNA_def_property_float_funcs(prop,
"rna_GpencilCurvePoint_BezTriple_handle1_get",
"rna_GpencilCurvePoint_BezTriple_handle1_set",
NULL);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Handle 1", "Coordinates of the first handle");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_update");
prop = RNA_def_property(srna, "co", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_array(prop, 3);
RNA_def_property_float_funcs(prop,
"rna_GpencilCurvePoint_BezTriple_ctrlpoint_get",
"rna_GpencilCurvePoint_BezTriple_ctrlpoint_set",
NULL);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Control Point", "Coordinates of the control point");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_update");
prop = RNA_def_property(srna, "handle_right", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_array(prop, 3);
RNA_def_property_float_funcs(prop,
"rna_GpencilCurvePoint_BezTriple_handle2_get",
"rna_GpencilCurvePoint_BezTriple_handle2_set",
NULL);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Handle 2", "Coordinates of the second handle");
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_update");
/* Pressure */
prop = RNA_def_property(srna, "pressure", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "pressure");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, RNA_TRANSLATION_PREC_DEFAULT);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Pressure", "Pressure of the grease pencil stroke point");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_update");
/* Strength */
prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "strength");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(
prop, "Strength", "Color intensity (alpha factor) of the grease pencil stroke point");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_update");
/* read-only index */
prop = RNA_def_property(srna, "point_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, "point_index");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(
prop, "Point Index", "Index of the corresponding grease pencil stroke point");
prop = RNA_def_property(srna, "uv_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "uv_fac");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "UV Factor", "Internal UV factor");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_update");
prop = RNA_def_property(srna, "uv_rotation", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "uv_rot");
RNA_def_property_range(prop, -M_PI_2, M_PI_2);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "UV Rotation", "Internal UV factor for dot mode");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_update");
prop = RNA_def_property(srna, "vertex_color", PROP_FLOAT, PROP_COLOR);
RNA_def_property_float_sdna(prop, NULL, "vert_color");
RNA_def_property_array(prop, 4);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Vertex Color", "Vertex color of the grease pencil stroke point");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_update");
}
/* Editing Curve data. */
static void rna_def_gpencil_curve(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "GPencilEditCurve", NULL);
RNA_def_struct_sdna(srna, "bGPDcurve");
RNA_def_struct_ui_text(srna, "Edit Curve", "Edition Curve");
prop = RNA_def_property(srna, "curve_points", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "curve_points", "tot_curve_points");
RNA_def_property_struct_type(prop, "GPencilEditCurvePoint");
RNA_def_property_ui_text(prop, "Curve Points", "Curve data points");
prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_CURVE_SELECT);
RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_curve_select_set");
RNA_def_property_ui_text(prop, "Select", "Curve is selected for viewport editing");
RNA_def_property_update(prop, 0, "rna_GPencil_update");
}
static void rna_def_gpencil_mvert_group(BlenderRNA *brna)
{
StructRNA *srna;
@ -1180,6 +1512,12 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "GPencilTriangle");
RNA_def_property_ui_text(prop, "Triangles", "Triangulation data for HQ fill");
/* Edit Curve. */
prop = RNA_def_property(srna, "edit_curve", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "editcurve");
RNA_def_property_struct_type(prop, "GPencilEditCurve");
RNA_def_property_ui_text(prop, "Edit Curve", "Temporary data for Edit Curve");
/* Material Index */
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "mat_nr");
@ -1205,6 +1543,12 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Cyclic", "Enable cyclic drawing, closing the stroke");
RNA_def_property_update(prop, 0, "rna_GPencil_update");
/* The stroke has Curve Edit data. */
prop = RNA_def_property(srna, "has_edit_curve", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_stroke_has_edit_curve_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Has Curve Data", "Stroke has Curve data to edit shape");
/* Caps mode */
prop = RNA_def_property(srna, "start_cap_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "caps[0]");
@ -2017,6 +2361,47 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
"Scale conversion factor for pixel size (use larger values for thicker lines)");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "edit_curve_resolution", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "curve_edit_resolution");
RNA_def_property_range(prop, 1, 256);
RNA_def_property_ui_range(prop, 1, 64, 1, 1);
RNA_def_property_int_default(prop, GP_DEFAULT_CURVE_RESOLUTION);
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
RNA_def_property_ui_text(
prop,
"Curve Resolution",
"Number of segments generated between control points when editing strokes in curve mode");
RNA_def_property_update(
prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_resolution_update");
prop = RNA_def_property(srna, "use_adaptive_curve_resolution", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_CURVE_ADAPTIVE_RESOLUTION);
RNA_def_property_boolean_default(prop, true);
RNA_def_property_ui_text(prop,
"Adaptive Resolution",
"Set the resolution of each editcurve segment dynamically depending on "
"the length of the segment. The resolution is the number of points "
"generated per unit distance");
RNA_def_property_update(
prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_resolution_update");
/* Curve editing error threshold. */
prop = RNA_def_property(srna, "curve_edit_threshold", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "curve_edit_threshold");
RNA_def_property_range(prop, FLT_MIN, 10.0);
RNA_def_property_float_default(prop, GP_DEFAULT_CURVE_ERROR);
RNA_def_property_ui_text(prop, "Threshold", "Curve conversion error threshold");
RNA_def_property_ui_range(prop, FLT_MIN, 10.0, 2, 5);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
/* Curve editing corner angle. */
prop = RNA_def_property(srna, "curve_edit_corner_angle", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "curve_edit_corner_angle");
RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f));
RNA_def_property_float_default(prop, DEG2RADF(90.0f));
RNA_def_property_ui_text(prop, "Corner Angle", "Angle threshold to be treated as corners");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "use_multiedit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_MULTIEDIT);
RNA_def_property_ui_text(prop,
@ -2025,6 +2410,11 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
"(keyframes must be selected to be included)");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "use_curve_edit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_CURVE_EDIT_MODE);
RNA_def_property_ui_text(prop, "Curve Editing", "Edit strokes using curve handles");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_curve_edit_mode_toggle");
prop = RNA_def_property(srna, "use_autolock_layers", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_AUTOLOCK_LAYERS);
RNA_def_property_ui_text(
@ -2176,6 +2566,8 @@ void RNA_def_gpencil(BlenderRNA *brna)
rna_def_gpencil_stroke(brna);
rna_def_gpencil_stroke_point(brna);
rna_def_gpencil_triangle(brna);
rna_def_gpencil_curve(brna);
rna_def_gpencil_curve_point(brna);
rna_def_gpencil_mvert_group(brna);
}