diff options
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_edit.c')
-rw-r--r-- | source/blender/editors/gpencil/gpencil_edit.c | 290 |
1 files changed, 289 insertions, 1 deletions
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index e6ab6d061ea..280512a2dd3 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3960,6 +3960,293 @@ 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 bool keep = RNA_boolean_get(op->ptr, "keep"); + const int thickness = RNA_int_get(op->ptr, "thickness"); + + const int view_mode = RNA_enum_get(op->ptr, "view_mode"); + const int material_mode = RNA_enum_get(op->ptr, "material_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 (material_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. */ + const float ovr_thickness = keep ? thickness : 0.0f; + bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view( + rv3d, gpd, gpl, gps_duplicate, subdivisions, diff_mat, ovr_thickness); + gps_perimeter->flag &= ~GP_STROKE_SELECT; + /* Assign material. */ + switch (material_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->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 stroke thickness. */ + gps_perimeter->thickness = thickness; + + /* 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, + "material_mode", + material_mode, + GP_STROKE_USE_ACTIVE_MATERIAL, + "Material Mode", + ""); + + RNA_def_int(ot->srna, + "thickness", + 1, + 1, + 1000, + "Thickness", + "Thickness of the stroke perimeter", + 1, + 1000); + RNA_def_boolean(ot->srna, + "keep", + true, + "Keep Shape", + "Try to keep global shape when the stroke thickness change"); + + 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 */ @@ -4483,7 +4770,8 @@ void GPENCIL_OT_stroke_sample(wmOperatorType *ot) /* properties */ prop = RNA_def_float(ot->srna, "length", 0.1f, 0.0f, 100.0f, "Length", "", 0.0f, 100.0f); - prop = RNA_def_float(ot->srna, "sharp_threshold", 0.1f, 0.0f, M_PI, "Sharp Threshold", "", 0.0f, M_PI); + prop = RNA_def_float( + ot->srna, "sharp_threshold", 0.1f, 0.0f, M_PI, "Sharp Threshold", "", 0.0f, M_PI); /* avoid re-using last var */ RNA_def_property_flag(prop, PROP_SKIP_SAVE); } |