From 29f3af95272590d26f610ae828b2eeee89c82a00 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 9 Mar 2020 16:27:24 +0100 Subject: GPencil: Refactor of Draw Engine, Vertex Paint and all internal functions This commit is a full refactor of the grease pencil modules including Draw Engine, Modifiers, VFX, depsgraph update, improvements in operators and conversion of Sculpt and Weight paint tools to real brushes. Also, a huge code cleanup has been done at all levels. Thanks to @fclem for his work and yo @pepeland and @mendio for the testing and help in the development. Differential Revision: https://developer.blender.org/D6293 --- .../blender/editors/gpencil/gpencil_vertex_ops.c | 899 +++++++++++++++++++++ 1 file changed, 899 insertions(+) create mode 100644 source/blender/editors/gpencil/gpencil_vertex_ops.c (limited to 'source/blender/editors/gpencil/gpencil_vertex_ops.c') diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c new file mode 100644 index 00000000000..6a3eebf1baf --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c @@ -0,0 +1,899 @@ +/* + * 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) 2015, Blender Foundation + * This is a new part of Blender + * Brush based operators for editing Grease Pencil strokes + */ + +/** \file + * \ingroup edgpencil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_paint.h" +#include "BKE_report.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "UI_view2d.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "gpencil_intern.h" + +enum { + GP_PAINT_VERTEX_STROKE = 0, + GP_PAINT_VERTEX_FILL = 1, + GP_PAINT_VERTEX_BOTH = 2, +}; + +static const EnumPropertyItem gpencil_modesEnumPropertyItem_mode[] = { + {GP_PAINT_VERTEX_STROKE, "STROKE", 0, "Stroke", ""}, + {GP_PAINT_VERTEX_FILL, "FILL", 0, "Fill", ""}, + {GP_PAINT_VERTEX_BOTH, "BOTH", 0, "Both", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +/* Poll callback for stroke vertex paint operator. */ +static bool gp_vertexpaint_mode_poll(bContext *C) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + bGPdata *gpd = (bGPdata *)ob->data; + if (GPENCIL_VERTEX_MODE(gpd)) { + if (!(GPENCIL_ANY_VERTEX_MASK(ts->gpencil_selectmode_vertex))) { + return false; + } + + /* Any data to use. */ + if (gpd->layers.first) { + return true; + } + } + + return false; +} + +static int gp_vertexpaint_brightness_contrast_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + bool changed = false; + int i; + bGPDspoint *pt; + const int mode = RNA_enum_get(op->ptr, "mode"); + + float gain, offset; + { + float brightness = RNA_float_get(op->ptr, "brightness"); + float contrast = RNA_float_get(op->ptr, "contrast"); + brightness /= 100.0f; + float delta = contrast / 200.0f; + /* + * The algorithm is by Werner D. Streidt + * (http://visca.com/ffactory/archives/5-99/msg00021.html) + * Extracted of OpenCV demhist.c + */ + if (contrast > 0) { + gain = 1.0f - delta * 2.0f; + gain = 1.0f / max_ff(gain, FLT_EPSILON); + offset = gain * (brightness - delta); + } + else { + delta *= -1; + gain = max_ff(1.0f - delta * 2.0f, 0.0f); + offset = gain * brightness + delta; + } + } + + /* Loop all selected strokes. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + /* Fill color. */ + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + if (mode != GP_PAINT_VERTEX_STROKE) { + if (gps->vert_color_fill[3] > 0.0f) { + for (int i2 = 0; i2 < 3; i2++) { + gps->vert_color_fill[i2] = gain * gps->vert_color_fill[i2] + offset; + } + } + } + } + + /* Stroke points. */ + if (mode != GP_PAINT_VERTEX_FILL) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) { + for (int i2 = 0; i2 < 3; i2++) { + pt->vert_color[i2] = gain * pt->vert_color[i2] + offset; + } + } + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + 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_vertex_color_brightness_contrast(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Vertex Paint Bright/Contrast"; + ot->idname = "GPENCIL_OT_vertex_color_brightness_contrast"; + ot->description = "Adjust vertex color brightness/contrast"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_brightness_contrast_exec; + ot->poll = gp_vertexpaint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", ""); + const float min = -100, max = +100; + prop = RNA_def_float(ot->srna, "brightness", 0.0f, min, max, "Brightness", "", min, max); + prop = RNA_def_float(ot->srna, "contrast", 0.0f, min, max, "Contrast", "", min, max); + RNA_def_property_ui_range(prop, min, max, 1, 1); +} + +static int gp_vertexpaint_hsv_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + + bool changed = false; + int i; + bGPDspoint *pt; + float hsv[3]; + + const int mode = RNA_enum_get(op->ptr, "mode"); + float hue = RNA_float_get(op->ptr, "h"); + float sat = RNA_float_get(op->ptr, "s"); + float val = RNA_float_get(op->ptr, "v"); + + /* Loop all selected strokes. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + + /* Fill color. */ + if (mode != GP_PAINT_VERTEX_STROKE) { + if (gps->vert_color_fill[3] > 0.0f) { + + rgb_to_hsv_v(gps->vert_color_fill, hsv); + + hsv[0] += (hue - 0.5f); + if (hsv[0] > 1.0f) { + hsv[0] -= 1.0f; + } + else if (hsv[0] < 0.0f) { + hsv[0] += 1.0f; + } + hsv[1] *= sat; + hsv[2] *= val; + + hsv_to_rgb_v(hsv, gps->vert_color_fill); + } + } + + /* Stroke points. */ + if (mode != GP_PAINT_VERTEX_FILL) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) { + rgb_to_hsv_v(pt->vert_color, hsv); + + hsv[0] += (hue - 0.5f); + if (hsv[0] > 1.0f) { + hsv[0] -= 1.0f; + } + else if (hsv[0] < 0.0f) { + hsv[0] += 1.0f; + } + hsv[1] *= sat; + hsv[2] *= val; + + hsv_to_rgb_v(hsv, pt->vert_color); + } + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + 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_vertex_color_hsv(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Paint Hue Saturation Value"; + ot->idname = "GPENCIL_OT_vertex_color_hsv"; + ot->description = "Adjust vertex color HSV values"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_hsv_exec; + ot->poll = gp_vertexpaint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", ""); + RNA_def_float(ot->srna, "h", 0.5f, 0.0f, 1.0f, "Hue", "", 0.0f, 1.0f); + RNA_def_float(ot->srna, "s", 1.0f, 0.0f, 2.0f, "Saturation", "", 0.0f, 2.0f); + RNA_def_float(ot->srna, "v", 1.0f, 0.0f, 2.0f, "Value", "", 0.0f, 2.0f); +} + +static int gp_vertexpaint_invert_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + + bool changed = false; + int i; + bGPDspoint *pt; + + const int mode = RNA_enum_get(op->ptr, "mode"); + + /* Loop all selected strokes. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + /* Fill color. */ + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + if (mode != GP_PAINT_VERTEX_STROKE) { + if (gps->vert_color_fill[3] > 0.0f) { + for (int i2 = 0; i2 < 3; i2++) { + gps->vert_color_fill[i2] = 1.0f - gps->vert_color_fill[i2]; + } + } + } + } + + /* Stroke points. */ + if (mode != GP_PAINT_VERTEX_FILL) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) { + for (int i2 = 0; i2 < 3; i2++) { + pt->vert_color[i2] = 1.0f - pt->vert_color[i2]; + } + } + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + 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_vertex_color_invert(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Vertex Paint Invert"; + ot->idname = "GPENCIL_OT_vertex_color_invert"; + ot->description = "Invert RGB values"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_invert_exec; + ot->poll = gp_vertexpaint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", ""); +} + +static int gp_vertexpaint_levels_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + + bool changed = false; + int i; + bGPDspoint *pt; + + const int mode = RNA_enum_get(op->ptr, "mode"); + float gain = RNA_float_get(op->ptr, "gain"); + float offset = RNA_float_get(op->ptr, "offset"); + + /* Loop all selected strokes. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + + /* Fill color. */ + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + if (mode != GP_PAINT_VERTEX_STROKE) { + if (gps->vert_color_fill[3] > 0.0f) { + for (int i2 = 0; i2 < 3; i2++) { + gps->vert_color_fill[i2] = gain * (gps->vert_color_fill[i2] + offset); + } + } + } + } + + /* Stroke points. */ + if (mode != GP_PAINT_VERTEX_FILL) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) && (pt->vert_color[3] > 0.0f)) { + for (int i2 = 0; i < 3; i2++) { + pt->vert_color[i2] = gain * (pt->vert_color[i2] + offset); + } + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + 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_vertex_color_levels(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name = "Vertex Paint Levels"; + ot->idname = "GPENCIL_OT_vertex_color_levels"; + ot->description = "Adjust levels of vertex colors"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_levels_exec; + ot->poll = gp_vertexpaint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", ""); + + RNA_def_float( + ot->srna, "offset", 0.0f, -1.0f, 1.0f, "Offset", "Value to add to colors", -1.0f, 1.0f); + RNA_def_float( + ot->srna, "gain", 1.0f, 0.0f, FLT_MAX, "Gain", "Value to multiply colors by", 0.0f, 10.0f); +} + +static int gp_vertexpaint_set_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + Paint *paint = &ts->gp_vertexpaint->paint; + Brush *brush = brush = paint->brush; + + bool changed = false; + int i; + bGPDspoint *pt; + + const int mode = RNA_enum_get(op->ptr, "mode"); + float factor = RNA_float_get(op->ptr, "factor"); + + /* Loop all selected strokes. */ + GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { + + /* Fill color. */ + if (gps->flag & GP_STROKE_SELECT) { + changed = true; + if (mode != GP_PAINT_VERTEX_STROKE) { + copy_v3_v3(gps->vert_color_fill, brush->rgb); + gps->vert_color_fill[3] = factor; + } + } + + /* Stroke points. */ + if (mode != GP_PAINT_VERTEX_FILL) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + copy_v3_v3(pt->vert_color, brush->rgb); + pt->vert_color[3] = factor; + } + } + } + } + GP_EDITABLE_STROKES_END(gpstroke_iter); + + /* notifiers */ + if (changed) { + 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_vertex_color_set(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name = "Vertex Paint Set Color"; + ot->idname = "GPENCIL_OT_vertex_color_set"; + ot->description = "Set active color to all selected vertex"; + + /* api callbacks */ + ot->exec = gp_vertexpaint_set_exec; + ot->poll = gp_vertexpaint_mode_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* params */ + ot->prop = RNA_def_enum(ot->srna, "mode", gpencil_modesEnumPropertyItem_mode, 0, "Mode", ""); + RNA_def_float(ot->srna, "factor", 1.0f, 0.001f, 1.0f, "Factor", "Mix Factor", 0.001f, 1.0f); +} + +/* Helper to extract color from vertex color to create a palette. */ +static bool gp_extract_palette_from_vertex(bContext *C, const bool selected, const int threshold) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + bool done = false; + const float range = pow(10.0f, threshold); + float col[3]; + + GHash *color_table = BLI_ghash_int_new(__func__); + + /* Extract all colors. */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); + if (gp_style == NULL) { + continue; + } + + if ((selected) && ((gps->flag & GP_STROKE_SELECT) == 0)) { + continue; + } + + bool use_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW); + bool use_fill = (gp_style->flag & GP_MATERIAL_FILL_SHOW); + + /* Material is disabled. */ + if ((!use_fill) && (!use_stroke)) { + continue; + } + + /* Only solid strokes or stencil. */ + if ((use_stroke) && ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && + ((gp_style->flag & GP_MATERIAL_STROKE_PATTERN) == 0))) { + continue; + } + + /* Only solid fill. */ + if ((use_fill) && (gp_style->fill_style != GP_MATERIAL_FILL_STYLE_SOLID)) { + continue; + } + + /* Fill color. */ + if (gps->vert_color_fill[3] > 0.0f) { + col[0] = truncf(gps->vert_color_fill[0] * range) / range; + col[1] = truncf(gps->vert_color_fill[1] * range) / range; + col[2] = truncf(gps->vert_color_fill[2] * range) / range; + + uint key = rgb_to_cpack(col[0], col[1], col[2]); + + if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) { + BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); + } + } + + /* Read all points to get all colors. */ + bGPDspoint *pt; + int i; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + col[0] = truncf(pt->vert_color[0] * range) / range; + col[1] = truncf(pt->vert_color[1] * range) / range; + col[2] = truncf(pt->vert_color[2] * range) / range; + + uint key = rgb_to_cpack(col[0], col[1], col[2]); + if (!BLI_ghash_haskey(color_table, POINTER_FROM_INT(key))) { + BLI_ghash_insert(color_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key)); + } + } + } + } + } + CTX_DATA_END; + + /* Create the Palette. */ + done = BKE_palette_from_hash(bmain, color_table, ob->id.name + 2, true); + + /* Free memory. */ + BLI_ghash_free(color_table, NULL, NULL); + + return done; +} + +/* Convert Materials to Vertex Color. */ +typedef struct GPMatArray { + uint key; + Material *ma; + int index; +} GPMatArray; + +static uint get_material_type(MaterialGPencilStyle *gp_style, + bool use_stroke, + bool use_fill, + char *name) +{ + uint r_i = 0; + if ((use_stroke) && (use_fill)) { + switch (gp_style->mode) { + case GP_MATERIAL_MODE_LINE: { + r_i = 1; + strcpy(name, "Line Stroke-Fill"); + break; + } + case GP_MATERIAL_MODE_DOT: { + r_i = 2; + strcpy(name, "Dots Stroke-Fill"); + break; + } + case GP_MATERIAL_MODE_SQUARE: { + r_i = 3; + strcpy(name, "Squares Stroke-Fill"); + break; + } + default: + break; + } + } + else if (use_stroke) { + switch (gp_style->mode) { + case GP_MATERIAL_MODE_LINE: { + r_i = 4; + strcpy(name, "Line Stroke"); + break; + } + case GP_MATERIAL_MODE_DOT: { + r_i = 5; + strcpy(name, "Dots Stroke"); + break; + } + case GP_MATERIAL_MODE_SQUARE: { + r_i = 6; + strcpy(name, "Squares Stroke"); + break; + } + default: + break; + } + } + else { + r_i = 7; + strcpy(name, "Solid Fill"); + } + + /* Create key TSSSSFFFF (T: Type S: Stroke Alpha F: Fill Alpha) */ + r_i *= 1e8; + if (use_stroke) { + r_i += gp_style->stroke_rgba[3] * 1e7; + } + if (use_fill) { + r_i += gp_style->fill_rgba[3] * 1e3; + } + + return r_i; +} + +static bool gp_material_to_vertex_poll(bContext *C) +{ + /* only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + return true; +} + +static int gp_material_to_vertex_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (bGPdata *)ob->data; + const bool remove = RNA_boolean_get(op->ptr, "remove"); + const bool palette = RNA_boolean_get(op->ptr, "palette"); + const bool selected = RNA_boolean_get(op->ptr, "selected"); + + char name[32] = ""; + Material *ma = NULL; + GPMatArray *mat_elm = NULL; + int i; + + bool changed = false; + + short *totcol = BKE_object_material_len_p(ob); + if (totcol == 0) { + return OPERATOR_CANCELLED; + } + + /* These arrays hold all materials and index in the material slots for all combinations. */ + int totmat = *totcol; + GPMatArray *mat_table = MEM_calloc_arrayN(totmat, sizeof(GPMatArray), __func__); + + /* Update stroke material index. */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + + if ((selected) && ((gps->flag & GP_STROKE_SELECT) == 0)) { + continue; + } + + MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1); + if (gp_style == NULL) { + continue; + } + + bool use_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) && + (gp_style->stroke_rgba[3] > 0.0f)); + bool use_fill = ((gp_style->flag & GP_MATERIAL_FILL_SHOW) && + (gp_style->fill_rgba[3] > 0.0f)); + bool is_stencil = ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && + (gp_style->flag & GP_MATERIAL_STROKE_PATTERN)); + /* Material is disabled. */ + if ((!use_fill) && (!use_stroke)) { + continue; + } + + /* Only solid strokes or stencil. */ + if ((use_stroke) && ((gp_style->stroke_style == GP_MATERIAL_STROKE_STYLE_TEXTURE) && + ((gp_style->flag & GP_MATERIAL_STROKE_PATTERN) == 0))) { + continue; + } + + /* Only solid fill. */ + if ((use_fill) && (gp_style->fill_style != GP_MATERIAL_FILL_STYLE_SOLID)) { + continue; + } + + /* Only for no Stencil materials. */ + if (!is_stencil) { + /* Create material type unique key by type and alpha. */ + uint key = get_material_type(gp_style, use_stroke, use_fill, name); + + /* Check if material exist. */ + bool found = false; + for (i = 0; i < totmat; i++) { + mat_elm = &mat_table[i]; + if (mat_elm->ma == NULL) { + break; + } + if (key == mat_elm->key) { + found = true; + break; + } + } + + /* If not found create a new material. */ + if (!found) { + ma = BKE_gpencil_material_add(bmain, name); + if (use_stroke) { + ma->gp_style->flag |= GP_MATERIAL_STROKE_SHOW; + } + else { + ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; + } + + if (use_fill) { + ma->gp_style->flag |= GP_MATERIAL_FILL_SHOW; + } + else { + ma->gp_style->flag &= ~GP_MATERIAL_FILL_SHOW; + } + + ma->gp_style->stroke_rgba[3] = gp_style->stroke_rgba[3]; + ma->gp_style->fill_rgba[3] = gp_style->fill_rgba[3]; + + BKE_object_material_slot_add(bmain, ob); + BKE_object_material_assign(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF); + + mat_elm->key = key; + mat_elm->ma = ma; + mat_elm->index = ob->totcol - 1; + } + else { + mat_elm = &mat_table[i]; + } + + /* Update stroke */ + gps->mat_nr = mat_elm->index; + } + + changed = true; + copy_v3_v3(gps->vert_color_fill, gp_style->fill_rgba); + gps->vert_color_fill[3] = 1.0f; + + /* Update all points. */ + bGPDspoint *pt; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + copy_v3_v3(pt->vert_color, gp_style->stroke_rgba); + pt->vert_color[3] = 1.0f; + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + /* Free memory. */ + MEM_SAFE_FREE(mat_table); + + /* Generate a Palette. */ + if (palette) { + gp_extract_palette_from_vertex(C, selected, 1); + } + + /* Clean unused materials. */ + if (remove) { + WM_operator_name_call( + C, "OBJECT_OT_material_slot_remove_unused", WM_OP_INVOKE_REGION_WIN, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_material_to_vertex_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Convert Stroke Materials to Vertex Color"; + ot->idname = "GPENCIL_OT_material_to_vertex_color"; + ot->description = "Replace materials in strokes with Vertex Color"; + + /* api callbacks */ + ot->exec = gp_material_to_vertex_exec; + ot->poll = gp_material_to_vertex_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_boolean(ot->srna, + "remove", + true, + "Remove Unused Materiales", + "Remove any unused material after the conversion"); + RNA_def_boolean(ot->srna, "palette", true, "Create Palette", "Create a new palette with colors"); + RNA_def_boolean(ot->srna, "selected", false, "Only Selected", "Convert only selected strokes"); + RNA_def_int(ot->srna, "threshold", 3, 1, 4, "Threshold", "", 1, 4); +} + +/* Extract Palette from Vertex Color. */ +static bool gp_extract_palette_vertex_poll(bContext *C) +{ + /* only supported with grease pencil objects */ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + + return true; +} + +static int gp_extract_palette_vertex_exec(bContext *C, wmOperator *op) +{ + const bool selected = RNA_boolean_get(op->ptr, "selected"); + const int threshold = RNA_int_get(op->ptr, "threshold"); + + if (gp_extract_palette_from_vertex(C, selected, threshold)) { + BKE_reportf(op->reports, RPT_INFO, "Palette created"); + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Unable to find Vertex Information to create palette"); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_extract_palette_vertex(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Extract Palette from Vertex Color"; + ot->idname = "GPENCIL_OT_extract_palette_vertex"; + ot->description = "Extract all colors used in Grease Pencil Vertex and create a Palette"; + + /* api callbacks */ + ot->exec = gp_extract_palette_vertex_exec; + ot->poll = gp_extract_palette_vertex_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_boolean( + ot->srna, "selected", false, "Only Selected", "Convert only selected strokes"); + RNA_def_int(ot->srna, "threshold", 1, 1, 4, "Threshold", "", 1, 4); +} -- cgit v1.2.3