GPencil: New operator to convert strokes to perimeter.
This operator converts any stroke of gpencil with a center line into a stroke with the perimeter. It's possible to assign the active material, keep current or create a new material for all perimeters. The conversion is only done for strokes with a material using `Stroke`. Only `Fill` strokes are not converted. Known issues: As the perimter has not boolean implementation, some perimeters can be overlaped. This could be solved in the future when a new 2D boolean library will be developed. Reviewed By: mendio, pepeland, frogstomp Differential Revision: https://developer.blender.org/D15664
This commit is contained in:
parent
4d4a84bbeb
commit
aa7b2f1dd9
|
@ -5178,6 +5178,9 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu):
|
|||
layout.separator()
|
||||
layout.operator("gpencil.reset_transform_fill", text="Reset Fill Transform")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("gpencil.stroke_outline", text="Outline")
|
||||
|
||||
|
||||
class VIEW3D_MT_edit_gpencil_point(Menu):
|
||||
bl_label = "Point"
|
||||
|
|
|
@ -3960,6 +3960,269 @@ static int gpencil_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op))
|
|||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Stroke Perimeter from View Operator
|
||||
* \{ */
|
||||
|
||||
enum {
|
||||
GP_PERIMETER_VIEW = 0,
|
||||
GP_PERIMETER_FRONT = 1,
|
||||
GP_PERIMETER_SIDE = 2,
|
||||
GP_PERIMETER_TOP = 3,
|
||||
GP_PERIMETER_CAMERA = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
GP_STROKE_USE_ACTIVE_MATERIAL = 0,
|
||||
GP_STROKE_USE_CURRENT_MATERIAL = 1,
|
||||
GP_STROKE_USE_NEW_MATERIAL = 2,
|
||||
};
|
||||
|
||||
static int gpencil_stroke_outline_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
RegionView3D *rv3d = CTX_wm_region_view3d(C);
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
bGPdata *gpd = (bGPdata *)ob->data;
|
||||
const int subdivisions = RNA_int_get(op->ptr, "subdivisions");
|
||||
const float length = RNA_float_get(op->ptr, "length");
|
||||
|
||||
const int view_mode = RNA_enum_get(op->ptr, "view_mode");
|
||||
const int mode = RNA_enum_get(op->ptr, "mode");
|
||||
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
|
||||
|
||||
/* sanity checks */
|
||||
if (ELEM(NULL, gpd)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
||||
float viewmat[4][4], viewinv[4][4];
|
||||
copy_m4_m4(viewmat, rv3d->viewmat);
|
||||
copy_m4_m4(viewinv, rv3d->viewinv);
|
||||
|
||||
switch (view_mode) {
|
||||
case GP_PERIMETER_FRONT:
|
||||
unit_m4(rv3d->viewmat);
|
||||
rv3d->viewmat[1][1] = 0.0f;
|
||||
rv3d->viewmat[1][2] = -1.0f;
|
||||
|
||||
rv3d->viewmat[2][1] = 1.0f;
|
||||
rv3d->viewmat[2][2] = 0.0f;
|
||||
|
||||
rv3d->viewmat[3][2] = -10.0f;
|
||||
invert_m4_m4(rv3d->viewinv, rv3d->viewmat);
|
||||
break;
|
||||
case GP_PERIMETER_SIDE:
|
||||
zero_m4(rv3d->viewmat);
|
||||
rv3d->viewmat[0][2] = 1.0f;
|
||||
rv3d->viewmat[1][0] = 1.0f;
|
||||
rv3d->viewmat[2][1] = 1.0f;
|
||||
rv3d->viewmat[3][3] = 1.0f;
|
||||
invert_m4_m4(rv3d->viewinv, rv3d->viewmat);
|
||||
break;
|
||||
case GP_PERIMETER_TOP:
|
||||
unit_m4(rv3d->viewmat);
|
||||
unit_m4(rv3d->viewinv);
|
||||
break;
|
||||
case GP_PERIMETER_CAMERA: {
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *cam_ob = scene->camera;
|
||||
if (cam_ob != NULL) {
|
||||
invert_m4_m4(rv3d->viewmat, cam_ob->obmat);
|
||||
copy_m4_m4(rv3d->viewinv, cam_ob->obmat);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Untag strokes to be sure nothing is pending. */
|
||||
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
||||
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
|
||||
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
|
||||
gps->flag &= ~GP_STROKE_TAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Create a new material. */
|
||||
int mat_idx = 0;
|
||||
if (mode == GP_STROKE_USE_NEW_MATERIAL) {
|
||||
Material *ma = BKE_gpencil_object_material_new(bmain, ob, "Material", NULL);
|
||||
MaterialGPencilStyle *gp_style = ma->gp_style;
|
||||
|
||||
gp_style->flag |= GP_MATERIAL_FILL_SHOW;
|
||||
mat_idx = ob->totcol - 1;
|
||||
}
|
||||
|
||||
/* loop all selected strokes */
|
||||
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
||||
if (gpl->flag & GP_LAYER_HIDE) {
|
||||
continue;
|
||||
}
|
||||
/* Prepare transform matrix. */
|
||||
float diff_mat[4][4];
|
||||
BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat);
|
||||
|
||||
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))) {
|
||||
if (gpf == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||
if ((gps->flag & GP_STROKE_SELECT) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (gps->totpoints == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!ED_gpencil_stroke_material_visible(ob, gps)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
|
||||
const bool is_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0);
|
||||
|
||||
if (!is_stroke) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Duplicate the stroke to apply any layer thickness change. */
|
||||
bGPDstroke *gps_duplicate = BKE_gpencil_stroke_duplicate(gps, true, false);
|
||||
|
||||
/* Apply layer thickness change. */
|
||||
gps_duplicate->thickness += gpl->line_change;
|
||||
/* Apply object scale to thickness. */
|
||||
gps_duplicate->thickness *= mat4_to_scale(ob->obmat);
|
||||
CLAMP_MIN(gps_duplicate->thickness, 1.0f);
|
||||
|
||||
/* Stroke. */
|
||||
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
|
||||
rv3d, gpd, gpl, gps_duplicate, subdivisions, diff_mat);
|
||||
gps_perimeter->flag &= ~GP_STROKE_SELECT;
|
||||
/* Assign material. */
|
||||
switch (mode) {
|
||||
case GP_STROKE_USE_ACTIVE_MATERIAL: {
|
||||
if (ob->actcol - 1 < 0) {
|
||||
gps_perimeter->mat_nr = 0;
|
||||
}
|
||||
else {
|
||||
gps_perimeter->mat_nr = ob->actcol - 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GP_STROKE_USE_CURRENT_MATERIAL:
|
||||
gps_perimeter->mat_nr = gps_perimeter->mat_nr;
|
||||
break;
|
||||
case GP_STROKE_USE_NEW_MATERIAL:
|
||||
gps_perimeter->mat_nr = mat_idx;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Sample stroke. */
|
||||
if (length > 0.0f) {
|
||||
BKE_gpencil_stroke_sample(gpd, gps_perimeter, length, false, 0);
|
||||
}
|
||||
|
||||
/* Set pressure constant. */
|
||||
bGPDspoint *pt;
|
||||
for (int i = 0; i < gps_perimeter->totpoints; i++) {
|
||||
pt = &gps_perimeter->points[i];
|
||||
pt->pressure = 1.0f;
|
||||
}
|
||||
|
||||
/* Add perimeter stroke to frame. */
|
||||
BLI_insertlinkafter(&gpf->strokes, gps, gps_perimeter);
|
||||
|
||||
/* Tag original stroke to be removed. */
|
||||
gps->flag |= GP_STROKE_TAG;
|
||||
|
||||
/* Free Temp stroke. */
|
||||
BKE_gpencil_free_stroke(gps_duplicate);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/* If not multi-edit, exit loop. */
|
||||
if (!is_multiedit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Free old strokes. */
|
||||
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
||||
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
|
||||
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
|
||||
if (gps->flag & GP_STROKE_TAG) {
|
||||
BLI_remlink(&gpf->strokes, gps);
|
||||
BKE_gpencil_free_stroke(gps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Back to view matrix. */
|
||||
copy_m4_m4(rv3d->viewmat, viewmat);
|
||||
copy_m4_m4(rv3d->viewinv, viewinv);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_stroke_outline(wmOperatorType *ot)
|
||||
{
|
||||
static const EnumPropertyItem view_mode[] = {
|
||||
{GP_PERIMETER_VIEW, "VIEW", 0, "View", ""},
|
||||
{GP_PERIMETER_FRONT, "FRONT", 0, "Front", ""},
|
||||
{GP_PERIMETER_SIDE, "SIDE", 0, "Side", ""},
|
||||
{GP_PERIMETER_TOP, "TOP", 0, "Top", ""},
|
||||
{GP_PERIMETER_CAMERA, "CAMERA", 0, "Camera", ""},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
static const EnumPropertyItem material_mode[] = {
|
||||
{GP_STROKE_USE_ACTIVE_MATERIAL, "ACTIVE", 0, "Active Material", ""},
|
||||
{GP_STROKE_USE_CURRENT_MATERIAL, "KEEP", 0, "Keep Material", "Keep current stroke material"},
|
||||
{GP_STROKE_USE_NEW_MATERIAL, "NEW", 0, "New Material", ""},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Convert Stroke to Outline";
|
||||
ot->idname = "GPENCIL_OT_stroke_outline";
|
||||
ot->description = "Convert stroke to perimeter";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = gpencil_stroke_outline_exec;
|
||||
ot->poll = gpencil_stroke_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = RNA_def_enum(ot->srna, "view_mode", view_mode, GP_PERIMETER_VIEW, "View", "");
|
||||
RNA_def_enum(
|
||||
ot->srna, "mode", material_mode, GP_STROKE_USE_ACTIVE_MATERIAL, "Material Mode", "");
|
||||
|
||||
RNA_def_int(ot->srna, "subdivisions", 3, 0, 10, "Subdivisions", "", 0, 10);
|
||||
|
||||
RNA_def_float(ot->srna, "length", 0.0f, 0.0f, 100.0f, "Sample Length", "", 0.0f, 100.0f);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void GPENCIL_OT_recalc_geometry(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
|
|
|
@ -608,6 +608,7 @@ void GPENCIL_OT_stroke_merge_by_distance(struct wmOperatorType *ot);
|
|||
void GPENCIL_OT_stroke_merge_material(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_reset_vertex_color(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_normalize(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_outline(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot);
|
||||
|
|
|
@ -635,6 +635,7 @@ void ED_operatortypes_gpencil(void)
|
|||
WM_operatortype_append(GPENCIL_OT_stroke_merge_material);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_reset_vertex_color);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_normalize);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_outline);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_material_to_vertex_color);
|
||||
WM_operatortype_append(GPENCIL_OT_extract_palette_vertex);
|
||||
|
|
Loading…
Reference in New Issue