Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_paint.c
| Context not available. | |||||
| #include "PIL_time.h" | #include "PIL_time.h" | ||||
| #include "DNA_object_types.h" | |||||
| #include "DNA_scene_types.h" | |||||
| #include "DNA_gpencil_types.h" | |||||
| #include "DNA_brush_types.h" | |||||
| #include "DNA_windowmanager_types.h" | |||||
| #include "DNA_workspace_types.h" | |||||
| #include "BKE_main.h" | #include "BKE_main.h" | ||||
| #include "BKE_paint.h" | #include "BKE_paint.h" | ||||
| #include "BKE_gpencil.h" | #include "BKE_gpencil.h" | ||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "BKE_layer.h" | |||||
| #include "BKE_screen.h" | #include "BKE_screen.h" | ||||
| #include "BKE_tracking.h" | #include "BKE_tracking.h" | ||||
| #include "BKE_colortools.h" | #include "BKE_colortools.h" | ||||
| #include "BKE_workspace.h" | |||||
| #include "DNA_object_types.h" | |||||
| #include "DNA_scene_types.h" | |||||
| #include "DNA_gpencil_types.h" | |||||
| #include "DNA_brush_types.h" | |||||
| #include "DNA_windowmanager_types.h" | |||||
| #include "UI_view2d.h" | #include "UI_view2d.h" | ||||
| #include "ED_gpencil.h" | #include "ED_gpencil.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_object.h" | |||||
| #include "ED_view3d.h" | #include "ED_view3d.h" | ||||
| #include "ED_clip.h" | #include "ED_clip.h" | ||||
| Context not available. | |||||
| GP_PAINTFLAG_STROKEADDED = (1 << 1), | GP_PAINTFLAG_STROKEADDED = (1 << 1), | ||||
| GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), | GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), | ||||
| GP_PAINTFLAG_SELECTMASK = (1 << 3), | GP_PAINTFLAG_SELECTMASK = (1 << 3), | ||||
| GP_PAINTFLAG_HARD_ERASER = (1 << 4), | |||||
| GP_PAINTFLAG_STROKE_ERASER = (1 << 5), | |||||
| } eGPencil_PaintFlags; | } eGPencil_PaintFlags; | ||||
| Context not available. | |||||
| */ | */ | ||||
| typedef struct tGPsdata { | typedef struct tGPsdata { | ||||
| EvaluationContext eval_ctx; | EvaluationContext eval_ctx; | ||||
| bContext *C; | |||||
| Main *bmain; /* main database pointer */ | |||||
| Scene *scene; /* current scene from context */ | Scene *scene; /* current scene from context */ | ||||
| struct Depsgraph *graph; | struct Depsgraph *graph; | ||||
| Context not available. | |||||
| View2D *v2d; /* needed for GP_STROKE_2DSPACE */ | View2D *v2d; /* needed for GP_STROKE_2DSPACE */ | ||||
| rctf *subrect; /* for using the camera rect within the 3d view */ | rctf *subrect; /* for using the camera rect within the 3d view */ | ||||
| rctf subrect_data; | rctf subrect_data; | ||||
| GP_SpaceConversion gsc; /* settings to pass to gp_points_to_xy() */ | GP_SpaceConversion gsc; /* settings to pass to gp_points_to_xy() */ | ||||
| PointerRNA ownerPtr; /* pointer to owner of gp-datablock */ | PointerRNA ownerPtr; /* pointer to owner of gp-datablock */ | ||||
| Context not available. | |||||
| void *erasercursor; /* radial cursor data for drawing eraser */ | void *erasercursor; /* radial cursor data for drawing eraser */ | ||||
| bGPDpalettecolor *palettecolor; /* current palette color */ | /* Palette settings are only used for 3D view */ | ||||
| bGPDbrush *brush; /* current drawing brush */ | Palette *palette; /* current palette */ | ||||
| PaletteColor *palettecolor; /* current palette color */ | |||||
| bGPDbrush *brush; /* current drawing brush */ | |||||
| short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ | short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ | ||||
| int lock_axis; /* lock drawing to one axis */ | int lock_axis; /* lock drawing to one axis */ | ||||
| bool no_fill; /* the stroke is no fill mode */ | |||||
| short keymodifier; /* key used for invoking the operator */ | short keymodifier; /* key used for invoking the operator */ | ||||
| short shift; /* shift modifier flag */ | |||||
| ReportList *reports; | |||||
| } tGPsdata; | } tGPsdata; | ||||
| /* ------ */ | /* ------ */ | ||||
| Context not available. | |||||
| { | { | ||||
| BLI_assert(p->gpf->strokes.last != NULL); | BLI_assert(p->gpf->strokes.last != NULL); | ||||
| p->flags |= GP_PAINTFLAG_STROKEADDED; | p->flags |= GP_PAINTFLAG_STROKEADDED; | ||||
| /* drawing batch cache is dirty now */ | |||||
| BKE_gpencil_batch_cache_dirty(p->gpd); | |||||
| p->gpd->flag |= GP_DATA_CACHE_IS_DIRTY; | |||||
| } | } | ||||
| /* ------ */ | /* ------ */ | ||||
| Context not available. | |||||
| static int gpencil_draw_poll(bContext *C) | static int gpencil_draw_poll(bContext *C) | ||||
| { | { | ||||
| if (ED_operator_regionactive(C)) { | if (ED_operator_regionactive(C)) { | ||||
| /* check if current context can support GPencil data */ | ScrArea *sa = CTX_wm_area(C); | ||||
| if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { | if (!ELEM(sa->spacetype, SPACE_VIEW3D)) { | ||||
| /* check if Grease Pencil isn't already running */ | /* check if current context can support GPencil data */ | ||||
| if (ED_gpencil_session_active() == 0) | if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { | ||||
| return 1; | /* check if Grease Pencil isn't already running */ | ||||
| else | if (ED_gpencil_session_active() == 0) | ||||
| CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); | return 1; | ||||
| else | |||||
| CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); | |||||
| } | |||||
| else { | |||||
| CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); | |||||
| } | |||||
| return 0; | |||||
| } | } | ||||
| /* 3D Viewport */ | |||||
| else { | else { | ||||
| CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); | if (ED_gpencil_session_active() == 0) { | ||||
| return 1; | |||||
| } | |||||
| else { | |||||
| return 0; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| CTX_wm_operator_poll_msg_set(C, "Active region not set"); | CTX_wm_operator_poll_msg_set(C, "Active region not set"); | ||||
| return 0; | |||||
| } | } | ||||
| return 0; | |||||
| } | } | ||||
| /* check if projecting strokes into 3d-geometry in the 3D-View */ | /* check if projecting strokes into 3d-geometry in the 3D-View */ | ||||
| Context not available. | |||||
| static void gp_get_3d_reference(tGPsdata *p, float vec[3]) | static void gp_get_3d_reference(tGPsdata *p, float vec[3]) | ||||
| { | { | ||||
| View3D *v3d = p->sa->spacedata.first; | View3D *v3d = p->sa->spacedata.first; | ||||
| const float *fp = ED_view3d_cursor3d_get(p->scene, v3d); | Object *ob = NULL; | ||||
| /* the reference point used depends on the owner... */ | |||||
| #if 0 /* XXX: disabled for now, since we can't draw relative to the owner yet */ | |||||
| if (p->ownerPtr.type == &RNA_Object) { | if (p->ownerPtr.type == &RNA_Object) { | ||||
| Object *ob = (Object *)p->ownerPtr.data; | ob = (Object *)p->ownerPtr.data; | ||||
| /* active Object | |||||
| * - use relative distance of 3D-cursor from object center | |||||
| */ | |||||
| sub_v3_v3v3(vec, fp, ob->loc); | |||||
| } | |||||
| else | |||||
| #endif | |||||
| { | |||||
| /* use 3D-cursor */ | |||||
| copy_v3_v3(vec, fp); | |||||
| } | } | ||||
| ED_gp_get_drawing_reference(v3d, p->scene, ob, p->gpl, *p->align_flag, vec); | |||||
| } | } | ||||
| /* Stroke Editing ---------------------------- */ | /* Stroke Editing ---------------------------- */ | ||||
| /* check if the current mouse position is suitable for adding a new point */ | /* check if the current mouse position is suitable for adding a new point */ | ||||
| static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) | static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) | ||||
| { | { | ||||
| bGPDbrush *brush = p->brush; | |||||
| int dx = abs(mval[0] - pmval[0]); | int dx = abs(mval[0] - pmval[0]); | ||||
| int dy = abs(mval[1] - pmval[1]); | int dy = abs(mval[1] - pmval[1]); | ||||
| brush->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; | |||||
| /* if buffer is empty, just let this go through (i.e. so that dots will work) */ | /* if buffer is empty, just let this go through (i.e. so that dots will work) */ | ||||
| if (p->gpd->sbuffer_size == 0) | if (p->gpd->sbuffer_size == 0) { | ||||
| return true; | return true; | ||||
| } | |||||
| /* if lazy mouse, check minimum distance */ | |||||
| else if (GPENCIL_LAZY_MODE(brush, p->shift)) { | |||||
| brush->flag |= GP_BRUSH_STABILIZE_MOUSE_TEMP; | |||||
| if ((dx * dx + dy * dy) > (brush->lazy_radius * brush->lazy_radius)) { | |||||
| return true; | |||||
| } | |||||
| else { | |||||
| /* If the mouse is moving within the radius of the last move, | |||||
| * don't update the mouse position. This allows sharp turns. */ | |||||
| copy_v2_v2_int(p->mval, p->mvalo); | |||||
| return false; | |||||
| } | |||||
| } | |||||
| /* check if mouse moved at least certain distance on both axes (best case) | /* check if mouse moved at least certain distance on both axes (best case) | ||||
| * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand | * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand | ||||
| */ | */ | ||||
| Context not available. | |||||
| return false; | return false; | ||||
| } | } | ||||
| /* reproject the points of the stroke to a plane locked to axis to avoid stroke offset */ | |||||
| static void gp_project_points_to_plane(RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis) | |||||
| { | |||||
| float plane_normal[3]; | |||||
| float vn[3]; | |||||
| float ray[3]; | |||||
| float rpoint[3]; | |||||
| /* normal vector for a plane locked to axis */ | |||||
| zero_v3(plane_normal); | |||||
| plane_normal[axis] = 1.0f; | |||||
| /* Reproject the points in the plane */ | |||||
| for (int i = 0; i < gps->totpoints; i++) { | |||||
| bGPDspoint *pt = &gps->points[i]; | |||||
| /* get a vector from the point with the current view direction of the viewport */ | |||||
| ED_view3d_global_to_vector(rv3d, &pt->x, vn); | |||||
| /* calculate line extrem point to create a ray that cross the plane */ | |||||
| mul_v3_fl(vn, -50.0f); | |||||
| add_v3_v3v3(ray, &pt->x, vn); | |||||
| /* if the line never intersect, the point is not changed */ | |||||
| if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { | |||||
| copy_v3_v3(&pt->x, rpoint); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* reproject stroke to plane locked to axis in 3d cursor location */ | /* reproject stroke to plane locked to axis in 3d cursor location */ | ||||
| static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) | static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) | ||||
| { | { | ||||
| bGPdata *gpd = p->gpd; | bGPdata *gpd = p->gpd; | ||||
| Object *obact = (Object *)p->ownerPtr.data; | |||||
| float origin[3]; | float origin[3]; | ||||
| float cursor[3]; | float cursor[3]; | ||||
| RegionView3D *rv3d = p->ar->regiondata; | RegionView3D *rv3d = p->ar->regiondata; | ||||
| Context not available. | |||||
| return; | return; | ||||
| } | } | ||||
| /* get 3d cursor and set origin for locked axis only. Uses axis-1 because the enum for XYZ start with 1 */ | /* get drawing origin and copy */ | ||||
| gp_get_3d_reference(p, cursor); | gp_get_3d_reference(p, cursor); | ||||
| zero_v3(origin); | copy_v3_v3(origin, cursor); | ||||
| origin[p->lock_axis - 1] = cursor[p->lock_axis - 1]; | ED_gp_project_stroke_to_plane(obact, rv3d, gps, origin, p->lock_axis - 1, p->scene->toolsettings->gpencil_src); | ||||
| gp_project_points_to_plane(rv3d, gps, origin, p->lock_axis - 1); | |||||
| } | } | ||||
| /* convert screen-coordinates to buffer-coordinates */ | /* convert screen-coordinates to buffer-coordinates */ | ||||
| Context not available. | |||||
| bGPDbrush *brush = p->brush; | bGPDbrush *brush = p->brush; | ||||
| tGPspoint *pt; | tGPspoint *pt; | ||||
| ToolSettings *ts = p->scene->toolsettings; | ToolSettings *ts = p->scene->toolsettings; | ||||
| Object *obact = (Object *)p->ownerPtr.data; | |||||
| /* check painting mode */ | /* check painting mode */ | ||||
| if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { | if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { | ||||
| Context not available. | |||||
| else { | else { | ||||
| pt->pressure = 1.0f; | pt->pressure = 1.0f; | ||||
| } | } | ||||
| /* Apply jitter to position */ | /* Apply jitter to position */ | ||||
| if (brush->draw_jitter > 0.0f) { | if (brush->draw_jitter > 0.0f) { | ||||
| int r_mval[2]; | int r_mval[2]; | ||||
| Context not available. | |||||
| /* increment counters */ | /* increment counters */ | ||||
| gpd->sbuffer_size++; | gpd->sbuffer_size++; | ||||
| /* check if another operation can still occur */ | /* check if another operation can still occur */ | ||||
| if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX) | if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX) | ||||
| return GP_STROKEADD_FULL; | return GP_STROKEADD_FULL; | ||||
| Context not available. | |||||
| return GP_STROKEADD_NORMAL; | return GP_STROKEADD_NORMAL; | ||||
| } | } | ||||
| else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | ||||
| bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); | bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); | ||||
| /* get pointer to destination point */ | /* get pointer to destination point */ | ||||
| pt = (tGPspoint *)(gpd->sbuffer); | pt = (tGPspoint *)(gpd->sbuffer); | ||||
| Context not available. | |||||
| } | } | ||||
| pts = &gps->points[gps->totpoints - 1]; | pts = &gps->points[gps->totpoints - 1]; | ||||
| pts->totweight = 0; | |||||
| pts->weights = NULL; | |||||
| /* special case for poly lines: normally, | /* special case for poly lines: normally, | ||||
| * depth is needed only when creating new stroke from buffer, | * depth is needed only when creating new stroke from buffer, | ||||
| * but poly lines are converting to stroke instantly, | * but poly lines are converting to stroke instantly, | ||||
| Context not available. | |||||
| /* convert screen-coordinates to appropriate coordinates (and store them) */ | /* convert screen-coordinates to appropriate coordinates (and store them) */ | ||||
| gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); | gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); | ||||
| /* if axis locked, reproject to plane locked (only in 3d space) */ | /* reproject to plane (only in 3d space) */ | ||||
| if (p->lock_axis > GP_LOCKAXIS_NONE) { | gp_reproject_toplane(p, gps); | ||||
| gp_reproject_toplane(p, gps); | |||||
| } | |||||
| /* if parented change position relative to parent object */ | /* if parented change position relative to parent object */ | ||||
| if (gpl->parent != NULL) { | gp_apply_parent_point(obact, gpd, gpl, pts); | ||||
| gp_apply_parent_point(gpl, pts); | |||||
| } | |||||
| /* copy pressure and time */ | /* copy pressure and time */ | ||||
| pts->pressure = pt->pressure; | pts->pressure = pt->pressure; | ||||
| pts->strength = pt->strength; | pts->strength = pt->strength; | ||||
| pts->time = pt->time; | pts->time = pt->time; | ||||
| pts->totweight = 0; | |||||
| pts->weights = NULL; | |||||
| /* force fill recalc */ | /* force fill recalc */ | ||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | gps->flag |= GP_STROKE_RECALC_CACHES; | ||||
| /* drawing batch cache is dirty now */ | |||||
| BKE_gpencil_batch_cache_dirty(p->gpd); | |||||
| p->gpd->flag |= GP_DATA_CACHE_IS_DIRTY; | |||||
| } | } | ||||
| /* increment counters */ | /* increment counters */ | ||||
| Context not available. | |||||
| tGPspoint *ptc; | tGPspoint *ptc; | ||||
| bGPDbrush *brush = p->brush; | bGPDbrush *brush = p->brush; | ||||
| ToolSettings *ts = p->scene->toolsettings; | ToolSettings *ts = p->scene->toolsettings; | ||||
| Object *obact = (Object *)p->ownerPtr.data; | |||||
| int i, totelem; | int i, totelem; | ||||
| /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ | /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ | ||||
| Context not available. | |||||
| * interactive behavior | * interactive behavior | ||||
| */ | */ | ||||
| if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | ||||
| /* be sure to hide any lazy cursor */ | |||||
| ED_gpencil_toggle_brush_cursor(p->C, true, NULL); | |||||
| if (gp_stroke_added_check(p)) { | if (gp_stroke_added_check(p)) { | ||||
| return; | return; | ||||
| } | } | ||||
| Context not available. | |||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | gps->flag |= GP_STROKE_RECALC_CACHES; | ||||
| /* allocate enough memory for a continuous array for storage points */ | /* allocate enough memory for a continuous array for storage points */ | ||||
| int sublevel = brush->sublevel; | const int sublevel = brush->sublevel; | ||||
| int new_totpoints = gps->totpoints; | |||||
| gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); | |||||
| for (i = 0; i < sublevel; i++) { | |||||
| new_totpoints += new_totpoints - 1; | |||||
| } | |||||
| gps->points = MEM_callocN(sizeof(bGPDspoint) * new_totpoints, "gp_stroke_points"); | |||||
| /* initialize triangle memory to dummy data */ | /* initialize triangle memory to dummy data */ | ||||
| gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); | gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); | ||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | gps->flag |= GP_STROKE_RECALC_CACHES; | ||||
| gps->tot_triangles = 0; | gps->tot_triangles = 0; | ||||
| /* drawing batch cache is dirty now */ | |||||
| BKE_gpencil_batch_cache_dirty(p->gpd); | |||||
| p->gpd->flag |= GP_DATA_CACHE_IS_DIRTY; | |||||
| /* set pointer to first non-initialized point */ | /* set pointer to first non-initialized point */ | ||||
| pt = gps->points + (gps->totpoints - totelem); | pt = gps->points + (gps->totpoints - totelem); | ||||
| Context not available. | |||||
| /* convert screen-coordinates to appropriate coordinates (and store them) */ | /* convert screen-coordinates to appropriate coordinates (and store them) */ | ||||
| gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | ||||
| /* if axis locked, reproject to plane locked (only in 3d space) */ | /* reproject to plane (only in 3d space) */ | ||||
| if (p->lock_axis > GP_LOCKAXIS_NONE) { | gp_reproject_toplane(p, gps); | ||||
| gp_reproject_toplane(p, gps); | |||||
| } | |||||
| /* if parented change position relative to parent object */ | /* if parented change position relative to parent object */ | ||||
| if (gpl->parent != NULL) { | gp_apply_parent_point(obact, gpd, gpl, pt); | ||||
| gp_apply_parent_point(gpl, pt); | |||||
| } | |||||
| /* copy pressure and time */ | /* copy pressure and time */ | ||||
| pt->pressure = ptc->pressure; | pt->pressure = ptc->pressure; | ||||
| pt->strength = ptc->strength; | pt->strength = ptc->strength; | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| pt->time = ptc->time; | pt->time = ptc->time; | ||||
| pt->totweight = 0; | |||||
| pt->weights = NULL; | |||||
| pt++; | pt++; | ||||
| } | } | ||||
| Context not available. | |||||
| /* convert screen-coordinates to appropriate coordinates (and store them) */ | /* convert screen-coordinates to appropriate coordinates (and store them) */ | ||||
| gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | ||||
| /* if axis locked, reproject to plane locked (only in 3d space) */ | /* reproject to plane (only in 3d space) */ | ||||
| if (p->lock_axis > GP_LOCKAXIS_NONE) { | gp_reproject_toplane(p, gps); | ||||
| gp_reproject_toplane(p, gps); | |||||
| } | |||||
| /* if parented change position relative to parent object */ | /* if parented change position relative to parent object */ | ||||
| if (gpl->parent != NULL) { | gp_apply_parent_point(obact, gpd, gpl, pt); | ||||
| gp_apply_parent_point(gpl, pt); | |||||
| } | |||||
| /* copy pressure and time */ | /* copy pressure and time */ | ||||
| pt->pressure = ptc->pressure; | pt->pressure = ptc->pressure; | ||||
| pt->strength = ptc->strength; | pt->strength = ptc->strength; | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| pt->time = ptc->time; | pt->time = ptc->time; | ||||
| pt->totweight = 0; | |||||
| pt->weights = NULL; | |||||
| } | } | ||||
| } | } | ||||
| else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { | ||||
| Context not available. | |||||
| /* convert screen-coordinates to appropriate coordinates (and store them) */ | /* convert screen-coordinates to appropriate coordinates (and store them) */ | ||||
| gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); | ||||
| /* if axis locked, reproject to plane locked (only in 3d space) */ | /* reproject to plane (only in 3d space) */ | ||||
| if (p->lock_axis > GP_LOCKAXIS_NONE) { | gp_reproject_toplane(p, gps); | ||||
| gp_reproject_toplane(p, gps); | |||||
| } | |||||
| /* if parented change position relative to parent object */ | /* if parented change position relative to parent object */ | ||||
| if (gpl->parent != NULL) { | gp_apply_parent_point(obact, gpd, gpl, pt); | ||||
| gp_apply_parent_point(gpl, pt); | |||||
| } | |||||
| /* copy pressure and time */ | /* copy pressure and time */ | ||||
| pt->pressure = ptc->pressure; | pt->pressure = ptc->pressure; | ||||
| pt->strength = ptc->strength; | pt->strength = ptc->strength; | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| pt->time = ptc->time; | pt->time = ptc->time; | ||||
| pt->totweight = 0; | |||||
| pt->weights = NULL; | |||||
| } | } | ||||
| else { | else { | ||||
| float *depth_arr = NULL; | float *depth_arr = NULL; | ||||
| Context not available. | |||||
| pt->strength = ptc->strength; | pt->strength = ptc->strength; | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | ||||
| pt->time = ptc->time; | pt->time = ptc->time; | ||||
| pt->totweight = 0; | |||||
| pt->weights = NULL; | |||||
| } | } | ||||
| /* subdivide the stroke */ | /* subdivide and smooth the stroke */ | ||||
| if (sublevel > 0) { | if (sublevel > 0) { | ||||
| int totpoints = gps->totpoints; | gp_subdivide_stroke(gps, sublevel); | ||||
| for (i = 0; i < sublevel; i++) { | |||||
| /* we're adding one new point between each pair of verts on each step */ | |||||
| totpoints += totpoints - 1; | |||||
| gp_subdivide_stroke(gps, totpoints); | |||||
| } | |||||
| } | } | ||||
| /* apply randomness to stroke */ | /* apply randomness to stroke */ | ||||
| if (brush->draw_random_sub > 0.0f) { | if (brush->draw_random_sub > 0.0f) { | ||||
| Context not available. | |||||
| */ | */ | ||||
| if (brush->draw_smoothfac > 0.0f) { | if (brush->draw_smoothfac > 0.0f) { | ||||
| float reduce = 0.0f; | float reduce = 0.0f; | ||||
| for (int r = 0; r < brush->draw_smoothlvl; ++r) { | for (int r = 0; r < brush->draw_smoothlvl; r++) { | ||||
| for (i = 0; i < gps->totpoints; i++) { | for (i = 0; i < gps->totpoints; i++) { | ||||
| /* NOTE: No pressure smoothing, or else we get annoying thickness changes while drawing... */ | BKE_gp_smooth_stroke(gps, i, brush->draw_smoothfac - reduce, false); | ||||
| gp_smooth_stroke(gps, i, brush->draw_smoothfac - reduce, false); | BKE_gp_smooth_stroke_strength(gps, i, brush->draw_smoothfac); | ||||
| } | } | ||||
| reduce += 0.25f; // reduce the factor | reduce += 0.25f; // reduce the factor | ||||
| } | } | ||||
| } | } | ||||
| /* smooth thickness */ | |||||
| /* if axis locked, reproject to plane locked (only in 3d space) */ | if (brush->thick_smoothfac > 0.0f) { | ||||
| if (p->lock_axis > GP_LOCKAXIS_NONE) { | for (int r = 0; r < brush->thick_smoothlvl * 2; r++) { | ||||
| gp_reproject_toplane(p, gps); | for (i = 0; i < gps->totpoints; i++) { | ||||
| } | BKE_gp_smooth_stroke_thickness(gps, i, brush->thick_smoothfac); | ||||
| /* if parented change position relative to parent object */ | } | ||||
| if (gpl->parent != NULL) { | } | ||||
| gp_apply_parent(gpl, gps); | |||||
| } | } | ||||
| /* reproject to plane (only in 3d space) */ | |||||
| gp_reproject_toplane(p, gps); | |||||
| /* change position relative to parent object */ | |||||
| gp_apply_parent(obact, gpd, gpl, gps); | |||||
| if (depth_arr) | if (depth_arr) | ||||
| MEM_freeN(depth_arr); | MEM_freeN(depth_arr); | ||||
| } | } | ||||
| /* Save palette color */ | /* Save palette color */ | ||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(p->gpd); | gps->palette = p->palette; | ||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | gps->palcolor = p->palettecolor; | ||||
| gps->palcolor = palcolor; | if (p->palettecolor) | ||||
| BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); | BLI_strncpy(gps->colorname, p->palettecolor->info, sizeof(gps->colorname)); | ||||
| /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke is added on listbase head | /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke is added on listbase head | ||||
| * because the drawing order is inverse and the head stroke is the first to draw. This is very useful for artist | * because the drawing order is inverse and the head stroke is the first to draw. This is very useful for artist | ||||
| Context not available. | |||||
| /* only erase stroke points that are visible */ | /* only erase stroke points that are visible */ | ||||
| static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y) | static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y) | ||||
| { | { | ||||
| Object *obact = (Object *)p->ownerPtr.data; | |||||
| if ((p->sa->spacetype == SPACE_VIEW3D) && | if ((p->sa->spacetype == SPACE_VIEW3D) && | ||||
| (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) | (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) | ||||
| { | { | ||||
| Context not available. | |||||
| float diff_mat[4][4]; | float diff_mat[4][4]; | ||||
| /* calculate difference matrix if parent object */ | /* calculate difference matrix if parent object */ | ||||
| ED_gpencil_parent_location(gpl, diff_mat); | ED_gpencil_parent_location(obact, p->gpd, gpl, diff_mat); | ||||
| if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) { | if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) { | ||||
| const float depth_mval = view3d_point_depth(rv3d, mval_3d); | const float depth_mval = view3d_point_depth(rv3d, mval_3d); | ||||
| Context not available. | |||||
| return fac; | return fac; | ||||
| } | } | ||||
| /* helper to free a stroke */ | |||||
| static void gp_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps) | |||||
| { | |||||
| if (gps->points) { | |||||
| BKE_gpencil_free_stroke_weights(gps); | |||||
| MEM_freeN(gps->points); | |||||
| } | |||||
| if (gps->triangles) | |||||
| MEM_freeN(gps->triangles); | |||||
| BLI_freelinkN(&gpf->strokes, gps); | |||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| } | |||||
| /* eraser tool - evaluation per stroke */ | /* eraser tool - evaluation per stroke */ | ||||
| /* TODO: this could really do with some optimization (KD-Tree/BVH?) */ | /* TODO: this could really do with some optimization (KD-Tree/BVH?) */ | ||||
| static void gp_stroke_eraser_dostroke(tGPsdata *p, | static void gp_stroke_eraser_dostroke(tGPsdata *p, | ||||
| Context not available. | |||||
| const int mval[2], const int mvalo[2], | const int mval[2], const int mvalo[2], | ||||
| const int radius, const rcti *rect) | const int radius, const rcti *rect) | ||||
| { | { | ||||
| Object *obact = (Object *)p->ownerPtr.data; | |||||
| bGPDspoint *pt1, *pt2; | bGPDspoint *pt1, *pt2; | ||||
| int pc1[2] = {0}; | int pc1[2] = {0}; | ||||
| int pc2[2] = {0}; | int pc2[2] = {0}; | ||||
| int i; | int i; | ||||
| float diff_mat[4][4]; | float diff_mat[4][4]; | ||||
| /* calculate difference matrix if parent object */ | /* calculate difference matrix */ | ||||
| if (gpl->parent != NULL) { | ED_gpencil_parent_location(obact, p->gpd, gpl, diff_mat); | ||||
| ED_gpencil_parent_location(gpl, diff_mat); | |||||
| } | |||||
| if (gps->totpoints == 0) { | if (gps->totpoints == 0) { | ||||
| /* just free stroke */ | /* just free stroke */ | ||||
| if (gps->points) | gp_free_stroke(p->gpd, gpf, gps); | ||||
| MEM_freeN(gps->points); | |||||
| if (gps->triangles) | |||||
| MEM_freeN(gps->triangles); | |||||
| BLI_freelinkN(&gpf->strokes, gps); | |||||
| } | } | ||||
| else if (gps->totpoints == 1) { | else if (gps->totpoints == 1) { | ||||
| /* only process if it hasn't been masked out... */ | /* only process if it hasn't been masked out... */ | ||||
| if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { | if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { | ||||
| if (gpl->parent == NULL) { | bGPDspoint pt_temp; | ||||
| gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); | gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); | ||||
| } | gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); | ||||
| else { | /* do boundbox check first */ | ||||
| bGPDspoint pt_temp; | if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { | ||||
| gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); | /* only check if point is inside */ | ||||
| gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); | if (len_v2v2_int(mval, pc1) <= radius) { | ||||
| /* free stroke */ | |||||
| gp_free_stroke(p->gpd, gpf, gps); | |||||
| } | |||||
| } | } | ||||
| } | |||||
| } | |||||
| else if (p->flags & GP_PAINTFLAG_STROKE_ERASER) { | |||||
| for (i = 0; (i + 1) < gps->totpoints; i++) { | |||||
| /* only process if it hasn't been masked out... */ | |||||
| if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) | |||||
| continue; | |||||
| /* get points to work with */ | |||||
| pt1 = gps->points + i; | |||||
| bGPDspoint npt; | |||||
| gp_point_to_parent_space(pt1, diff_mat, &npt); | |||||
| gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); | |||||
| /* do boundbox check first */ | /* do boundbox check first */ | ||||
| if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { | if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { | ||||
| /* only check if point is inside */ | /* only check if point is inside */ | ||||
| if (len_v2v2_int(mval, pc1) <= radius) { | if (len_v2v2_int(mval, pc1) <= radius) { | ||||
| /* free stroke */ | /* free stroke */ | ||||
| // XXX: pressure sensitive eraser should apply here too? | gp_free_stroke(p->gpd, gpf, gps); | ||||
| MEM_freeN(gps->points); | return; | ||||
| if (gps->triangles) | |||||
| MEM_freeN(gps->triangles); | |||||
| BLI_freelinkN(&gpf->strokes, gps); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) | if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) | ||||
| continue; | continue; | ||||
| if (gpl->parent == NULL) { | bGPDspoint npt; | ||||
| gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); | gp_point_to_parent_space(pt1, diff_mat, &npt); | ||||
| gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); | gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); | ||||
| } | |||||
| else { | |||||
| bGPDspoint npt; | |||||
| gp_point_to_parent_space(pt1, diff_mat, &npt); | |||||
| gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); | |||||
| gp_point_to_parent_space(pt2, diff_mat, &npt); | gp_point_to_parent_space(pt2, diff_mat, &npt); | ||||
| gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); | gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); | ||||
| } | |||||
| /* Check that point segment of the boundbox of the eraser stroke */ | /* Check that point segment of the boundbox of the eraser stroke */ | ||||
| if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || | if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || | ||||
| Context not available. | |||||
| pt2->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc2) * strength / 2.0f; | pt2->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc2) * strength / 2.0f; | ||||
| /* 2) Tag any point with overly low influence for removal in the next pass */ | /* 2) Tag any point with overly low influence for removal in the next pass */ | ||||
| if (pt1->pressure < cull_thresh) { | if ((pt1->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER)) { | ||||
| pt1->flag |= GP_SPOINT_TAG; | pt1->flag |= GP_SPOINT_TAG; | ||||
| do_cull = true; | do_cull = true; | ||||
| } | } | ||||
| if (pt2->pressure < cull_thresh) { | if ((pt2->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER)) { | ||||
| pt2->flag |= GP_SPOINT_TAG; | pt2->flag |= GP_SPOINT_TAG; | ||||
| do_cull = true; | do_cull = true; | ||||
| } | } | ||||
| Context not available. | |||||
| /* Second Pass: Remove any points that are tagged */ | /* Second Pass: Remove any points that are tagged */ | ||||
| if (do_cull) { | if (do_cull) { | ||||
| gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG); | gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false); | ||||
| } | } | ||||
| BKE_gpencil_batch_cache_dirty(p->gpd); | |||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| static void gp_session_validatebuffer(tGPsdata *p) | static void gp_session_validatebuffer(tGPsdata *p) | ||||
| { | { | ||||
| bGPdata *gpd = p->gpd; | bGPdata *gpd = p->gpd; | ||||
| bGPDbrush *brush = p->brush; | |||||
| /* clear memory of buffer (or allocate it if starting a new session) */ | /* clear memory of buffer (or allocate it if starting a new session) */ | ||||
| if (gpd->sbuffer) { | if (gpd->sbuffer) { | ||||
| Context not available. | |||||
| /* reset inittime */ | /* reset inittime */ | ||||
| p->inittime = 0.0; | p->inittime = 0.0; | ||||
| } | |||||
| /* create a new palette color */ | /* reset lazy */ | ||||
| static bGPDpalettecolor *gp_create_new_color(bGPDpalette *palette) | if (brush) { | ||||
| { | brush->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; | ||||
| bGPDpalettecolor *palcolor; | } | ||||
| palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); | |||||
| return palcolor; | |||||
| } | } | ||||
| /* initialize a drawing brush */ | /* initialize a drawing brush */ | ||||
| Context not available. | |||||
| /* initialize a paint palette brush and a default color if not exist */ | /* initialize a paint palette brush and a default color if not exist */ | ||||
| static void gp_init_palette(tGPsdata *p) | static void gp_init_palette(tGPsdata *p) | ||||
| { | { | ||||
| bGPdata *gpd; | bGPdata *gpd = p->gpd; | ||||
| bGPDpalette *palette; | |||||
| bGPDpalettecolor *palcolor; | bGPDpaletteref *palslot; | ||||
| Palette *palette = NULL; | |||||
| gpd = p->gpd; | PaletteColor *palcolor = NULL; | ||||
| /* if not exist, create a new palette */ | /* get palette and color info | ||||
| if (BLI_listbase_is_empty(&gpd->palettes)) { | * NOTE: _validate() ensures that everything we need will exist... | ||||
| /* create new palette */ | */ | ||||
| palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); | palslot = BKE_gpencil_paletteslot_validate(p->bmain, gpd); | ||||
| /* now create a default color */ | palette = palslot->palette; | ||||
| palcolor = gp_create_new_color(palette); | palcolor = BKE_palette_color_get_active(palette); | ||||
| } | |||||
| else { | /* assign color to temp tGPsdata */ | ||||
| /* Use the current palette and color */ | if (palcolor) { | ||||
| palette = BKE_gpencil_palette_getactive(gpd); | p->palette = palette; | ||||
| /* the palette needs one color */ | p->palettecolor = palcolor; | ||||
| if (BLI_listbase_is_empty(&palette->colors)) { | |||||
| palcolor = gp_create_new_color(palette); | /* set palette colors */ | ||||
| } | copy_v4_v4(gpd->scolor, palcolor->rgb); | ||||
| else { | copy_v4_v4(gpd->sfill, palcolor->fill); | ||||
| palcolor = BKE_gpencil_palettecolor_getactive(palette); | /* add some alpha to make easy the filling without hide strokes */ | ||||
| } | if (gpd->sfill[3] > 0.8f) { | ||||
| /* in some situations can be null, so use first */ | gpd->sfill[3] = 0.8f; | ||||
| if (palcolor == NULL) { | |||||
| BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); | |||||
| palcolor = palette->colors.first; | |||||
| } | } | ||||
| } | |||||
| /* asign to temp tGPsdata */ | gpd->sflag = palcolor->flag; | ||||
| p->palettecolor = palcolor; | gpd->bstroke_style = palcolor->stroke_style; | ||||
| gpd->bfill_style = palcolor->fill_style; | |||||
| } | |||||
| } | } | ||||
| /* (re)init new painting data */ | /* (re)init new painting data */ | ||||
| static bool gp_session_initdata(bContext *C, tGPsdata *p) | static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) | ||||
| { | { | ||||
| bGPdata **gpd_ptr = NULL; | bGPdata **gpd_ptr = NULL; | ||||
| ScrArea *curarea = CTX_wm_area(C); | ScrArea *curarea = CTX_wm_area(C); | ||||
| ARegion *ar = CTX_wm_region(C); | ARegion *ar = CTX_wm_region(C); | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| Object *obact = CTX_data_active_object(C); | |||||
| View3D *v3d = curarea->spacedata.first; | |||||
| /* make sure the active view (at the starting time) is a 3d-view */ | /* make sure the active view (at the starting time) is a 3d-view */ | ||||
| if (curarea == NULL) { | if (curarea == NULL) { | ||||
| p->status = GP_STATUS_ERROR; | p->status = GP_STATUS_ERROR; | ||||
| Context not available. | |||||
| /* pass on current scene and window */ | /* pass on current scene and window */ | ||||
| CTX_data_eval_ctx(C, &p->eval_ctx); | CTX_data_eval_ctx(C, &p->eval_ctx); | ||||
| p->C = C; | |||||
| p->bmain = CTX_data_main(C); | |||||
| p->scene = CTX_data_scene(C); | p->scene = CTX_data_scene(C); | ||||
| p->graph = CTX_data_depsgraph(C); | p->graph = CTX_data_depsgraph(C); | ||||
| p->win = CTX_wm_window(C); | p->win = CTX_wm_window(C); | ||||
| p->no_fill = RNA_boolean_get(op->ptr, "no_fill"); | |||||
| unit_m4(p->imat); | unit_m4(p->imat); | ||||
| unit_m4(p->mat); | unit_m4(p->mat); | ||||
| Context not available. | |||||
| { | { | ||||
| /* View3D *v3d = curarea->spacedata.first; */ | /* View3D *v3d = curarea->spacedata.first; */ | ||||
| /* RegionView3D *rv3d = ar->regiondata; */ | /* RegionView3D *rv3d = ar->regiondata; */ | ||||
| /* set current area | /* set current area | ||||
| * - must verify that region data is 3D-view (and not something else) | * - must verify that region data is 3D-view (and not something else) | ||||
| */ | */ | ||||
| /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ | /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ | ||||
| p->sa = curarea; | p->sa = curarea; | ||||
| p->ar = ar; | p->ar = ar; | ||||
| p->align_flag = &ts->gpencil_v3d_align; | p->align_flag = &ts->gpencil_v3d_align; | ||||
| if (ar->regiondata == NULL) { | if (ar->regiondata == NULL) { | ||||
| p->status = GP_STATUS_ERROR; | p->status = GP_STATUS_ERROR; | ||||
| if (G.debug & G_DEBUG) | if (G.debug & G_DEBUG) | ||||
| printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable\n"); | printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable\n"); | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* if active object doesn't exist or it's not a Grease Pencil object, | |||||
| * use the scene's gp_object (), or create one if it doesn't exist | |||||
| */ | |||||
| float *cur = ED_view3d_cursor3d_get(p->scene, v3d); | |||||
| if ((!obact) || (obact->type != OB_GPENCIL)) { | |||||
| if (p->scene->gp_object) { | |||||
| /* use existing default */ | |||||
| /* XXX: This will still lose whatever mode we were in before, | |||||
| * making GP less convenient for annotations than it used to be | |||||
| */ | |||||
| obact = p->scene->gp_object; | |||||
| /* temporarily activate the object */ | |||||
| ViewLayer *view_layer = CTX_data_view_layer(C); | |||||
| Base *base = BKE_view_layer_base_find(view_layer, obact); | |||||
| if (base) { | |||||
| if (CTX_data_edit_object(C)) | |||||
| ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO); /* freedata, and undo */ | |||||
| view_layer->basact = base; | |||||
| ED_object_base_activate(C, base); | |||||
| } | |||||
| else { | |||||
| printf("ERROR: Couldn't find base for active gp_object (view_layer = %p, obact = %s)\n", view_layer, obact->id.name); | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* create new default */ | |||||
| obact = ED_add_gpencil_object(C, p->scene, cur); | |||||
| p->scene->gp_object = obact; | |||||
| } | |||||
| } | |||||
| break; | break; | ||||
| } | } | ||||
| case SPACE_NODE: | case SPACE_NODE: | ||||
| Context not available. | |||||
| else { | else { | ||||
| /* if no existing GPencil block exists, add one */ | /* if no existing GPencil block exists, add one */ | ||||
| if (*gpd_ptr == NULL) | if (*gpd_ptr == NULL) | ||||
| *gpd_ptr = BKE_gpencil_data_addnew("GPencil"); | *gpd_ptr = BKE_gpencil_data_addnew(CTX_data_main(C), "GPencil"); | ||||
| p->gpd = *gpd_ptr; | p->gpd = *gpd_ptr; | ||||
| } | } | ||||
| Context not available. | |||||
| /* clear out buffer (stored in gp-data), in case something contaminated it */ | /* clear out buffer (stored in gp-data), in case something contaminated it */ | ||||
| gp_session_validatebuffer(p); | gp_session_validatebuffer(p); | ||||
| /* set brush and create a new one if null */ | /* set brush and create a new one if null */ | ||||
| gp_init_drawing_brush(ts, p); | gp_init_drawing_brush(ts, p); | ||||
| /* set palette info and create a new one if null */ | |||||
| gp_init_palette(p); | /* setup active palette */ | ||||
| /* set palette colors */ | if (curarea->spacetype == SPACE_VIEW3D) { | ||||
| bGPDpalettecolor *palcolor = p->palettecolor; | /* NOTE: This is only done for 3D view, as Palettes aren't used for | ||||
| bGPdata *pdata = p->gpd; | * annotations in 2D editors | ||||
| copy_v4_v4(pdata->scolor, palcolor->color); | */ | ||||
| copy_v4_v4(pdata->sfill, palcolor->fill); | gp_init_palette(p); | ||||
| pdata->sflag = palcolor->flag; | } | ||||
| /* lock axis */ | /* lock axis */ | ||||
| p->lock_axis = ts->gp_sculpt.lock_axis; | p->lock_axis = ts->gp_sculpt.lock_axis; | ||||
| Context not available. | |||||
| } | } | ||||
| /* init new painting session */ | /* init new painting session */ | ||||
| static tGPsdata *gp_session_initpaint(bContext *C) | static tGPsdata *gp_session_initpaint(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| tGPsdata *p = NULL; | tGPsdata *p = NULL; | ||||
| /* create new context data */ | /* create new context data */ | ||||
| p = MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data"); | p = MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data"); | ||||
| gp_session_initdata(C, p); | gp_session_initdata(C, op, p); | ||||
| /* radius for eraser circle is defined in userprefs now */ | /* radius for eraser circle is defined in userprefs now */ | ||||
| /* NOTE: we do this here, so that if we exit immediately, | /* NOTE: we do this here, so that if we exit immediately, | ||||
| Context not available. | |||||
| add_frame_mode = GP_GETFRAME_ADD_NEW; | add_frame_mode = GP_GETFRAME_ADD_NEW; | ||||
| p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); | p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); | ||||
| /* set as dirty draw manager cache */ | |||||
| BKE_gpencil_batch_cache_dirty(p->gpd); | |||||
| if (p->gpf == NULL) { | if (p->gpf == NULL) { | ||||
| p->status = GP_STATUS_ERROR; | p->status = GP_STATUS_ERROR; | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* set special fill stroke mode */ | |||||
| if (p->no_fill == true) { | |||||
| p->gpd->sbuffer_sflag |= GP_STROKE_NOFILL; | |||||
| /* replace stroke color with fill color */ | |||||
| copy_v4_v4(p->gpd->scolor, p->gpd->sfill); | |||||
| } | |||||
| /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ | /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ | ||||
| p->flags |= GP_PAINTFLAG_FIRSTRUN; | p->flags |= GP_PAINTFLAG_FIRSTRUN; | ||||
| /* when drawing in the camera view, in 2D space, set the subrect */ | /* when drawing in the camera view, in 2D space, set the subrect */ | ||||
| p->subrect = NULL; | p->subrect = NULL; | ||||
| if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { | if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { | ||||
| Context not available. | |||||
| static void gpencil_draw_exit(bContext *C, wmOperator *op) | static void gpencil_draw_exit(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| tGPsdata *p = op->customdata; | tGPsdata *p = op->customdata; | ||||
| bGPdata *gpd = CTX_data_gpencil_data(C); | |||||
| /* clear undo stack */ | /* clear undo stack */ | ||||
| gpencil_undo_finish(); | gpencil_undo_finish(); | ||||
| /* restore cursor to indicate end of drawing */ | /* restore cursor to indicate end of drawing */ | ||||
| WM_cursor_modal_restore(CTX_wm_window(C)); | if (p->sa->spacetype != SPACE_VIEW3D) { | ||||
| WM_cursor_modal_restore(CTX_wm_window(C)); | |||||
| } | |||||
| else { | |||||
| /* or restore paint if 3D view */ | |||||
| if ((p) && (p->paintmode == GP_PAINTMODE_ERASER)) { | |||||
| WM_cursor_modal_set(p->win, CURSOR_STD); | |||||
| } | |||||
| /* drawing batch cache is dirty now */ | |||||
| if (gpd) { | |||||
| BKE_gpencil_batch_cache_dirty(gpd); | |||||
| gpd->flag |= GP_DATA_CACHE_IS_DIRTY; | |||||
| } | |||||
| } | |||||
| /* don't assume that operator data exists at all */ | /* don't assume that operator data exists at all */ | ||||
| if (p) { | if (p) { | ||||
| /* check size of buffer before cleanup, to determine if anything happened here */ | /* check size of buffer before cleanup, to determine if anything happened here */ | ||||
| Context not available. | |||||
| /* cleanup */ | /* cleanup */ | ||||
| gp_paint_cleanup(p); | gp_paint_cleanup(p); | ||||
| gp_session_cleanup(p); | gp_session_cleanup(p); | ||||
| ED_gpencil_toggle_brush_cursor(C, true, NULL); | |||||
| /* finally, free the temp data */ | /* finally, free the temp data */ | ||||
| MEM_freeN(p); | MEM_freeN(p); | ||||
| } | } | ||||
| Context not available. | |||||
| eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); | eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); | ||||
| /* check context */ | /* check context */ | ||||
| p = op->customdata = gp_session_initpaint(C); | p = op->customdata = gp_session_initpaint(C, op); | ||||
| if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { | if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { | ||||
| /* something wasn't set correctly in context */ | /* something wasn't set correctly in context */ | ||||
| gpencil_draw_exit(C, op); | gpencil_draw_exit(C, op); | ||||
| Context not available. | |||||
| p->keymodifier = -1; | p->keymodifier = -1; | ||||
| } | } | ||||
| p->reports = op->reports; | |||||
| /* everything is now setup ok */ | /* everything is now setup ok */ | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| Context not available. | |||||
| if (p->paintmode == GP_PAINTMODE_ERASER) | if (p->paintmode == GP_PAINTMODE_ERASER) | ||||
| WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ | WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ | ||||
| else | else | ||||
| WM_cursor_modal_set(p->win, BC_PAINTBRUSHCURSOR); | WM_cursor_modal_set(p->win, CURSOR_STD); | ||||
| } | } | ||||
| /* update UI indicators of status, including cursor and header prints */ | /* update UI indicators of status, including cursor and header prints */ | ||||
| Context not available. | |||||
| "ESC/Enter to end (or click outside this area)")); | "ESC/Enter to end (or click outside this area)")); | ||||
| break; | break; | ||||
| case GP_PAINTMODE_DRAW: | case GP_PAINTMODE_DRAW: | ||||
| ED_area_headerprint(p->sa, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | " | ED_area_headerprint(p->sa, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw")); | ||||
| "E/ESC/Enter to end (or click outside this area)")); | |||||
| break; | break; | ||||
| case GP_PAINTMODE_DRAW_POLY: | case GP_PAINTMODE_DRAW_POLY: | ||||
| ED_area_headerprint(p->sa, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " | ED_area_headerprint(p->sa, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " | ||||
| "ESC/Enter to end (or click outside this area)")); | "Release Shift/ESC/Enter to end (or click outside this area)")); | ||||
| break; | break; | ||||
| default: /* unhandled future cases */ | default: /* unhandled future cases */ | ||||
| Context not available. | |||||
| /* ------------------------------- */ | /* ------------------------------- */ | ||||
| /* create a new stroke point at the point indicated by the painting context */ | /* create a new stroke point at the point indicated by the painting context */ | ||||
| static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, const Depsgraph *depsgraph) | static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, const Depsgraph *depsgraph) | ||||
| { | { | ||||
| bGPdata *gpd = p->gpd; | |||||
| tGPspoint *pt = NULL; | |||||
| /* handle drawing/erasing -> test for erasing first */ | /* handle drawing/erasing -> test for erasing first */ | ||||
| if (p->paintmode == GP_PAINTMODE_ERASER) { | if (p->paintmode == GP_PAINTMODE_ERASER) { | ||||
| /* do 'live' erasing now */ | /* do 'live' erasing now */ | ||||
| Context not available. | |||||
| } | } | ||||
| /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */ | /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */ | ||||
| else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { | else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { | ||||
| /* if lazy mouse, interpolate the last and current mouse positions */ | |||||
| if (GPENCIL_LAZY_MODE(p->brush, p->shift)) { | |||||
| float now_mouse[2]; | |||||
| float last_mouse[2]; | |||||
| copy_v2fl_v2i(now_mouse, p->mval); | |||||
| copy_v2fl_v2i(last_mouse, p->mvalo); | |||||
| interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->lazy_factor); | |||||
| round_v2i_v2fl(p->mval, now_mouse); | |||||
| } | |||||
| /* try to add point */ | /* try to add point */ | ||||
| short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); | short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); | ||||
| Context not available. | |||||
| p->mvalo[1] = p->mval[1]; | p->mvalo[1] = p->mval[1]; | ||||
| p->opressure = p->pressure; | p->opressure = p->pressure; | ||||
| p->ocurtime = p->curtime; | p->ocurtime = p->curtime; | ||||
| pt = (tGPspoint *)gpd->sbuffer + gpd->sbuffer_size - 1; | |||||
| ED_gpencil_toggle_brush_cursor(C, true, &pt->x); | |||||
| } | |||||
| else if ((p->brush->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) && (gpd->sbuffer_size > 0)){ | |||||
| pt = (tGPspoint *)gpd->sbuffer + gpd->sbuffer_size - 1; | |||||
| ED_gpencil_toggle_brush_cursor(C, true, &pt->x); | |||||
| } | } | ||||
| } | } | ||||
| /* handle draw event */ | /* handle draw event */ | ||||
| static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, const Depsgraph *depsgraph) | static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent *event, const Depsgraph *depsgraph) | ||||
| { | { | ||||
| tGPsdata *p = op->customdata; | tGPsdata *p = op->customdata; | ||||
| PointerRNA itemptr; | PointerRNA itemptr; | ||||
| Context not available. | |||||
| */ | */ | ||||
| p->mval[0] = event->mval[0] + 1; | p->mval[0] = event->mval[0] + 1; | ||||
| p->mval[1] = event->mval[1] + 1; | p->mval[1] = event->mval[1] + 1; | ||||
| p->shift = event->shift; | |||||
| /* verify key status for straight lines */ | /* verify key status for straight lines */ | ||||
| if ((event->ctrl > 0) || (event->alt > 0)) { | if ((event->ctrl > 0) && (RNA_boolean_get(op->ptr, "no_straight") == false)) { | ||||
| if (p->straight[0] == 0) { | if (p->straight[0] == 0) { | ||||
| int dx = abs(p->mval[0] - p->mvalo[0]); | int dx = abs(p->mval[0] - p->mvalo[0]); | ||||
| int dy = abs(p->mval[1] - p->mvalo[1]); | int dy = abs(p->mval[1] - p->mvalo[1]); | ||||
| Context not available. | |||||
| p->pressure = 1.0f; | p->pressure = 1.0f; | ||||
| } | } | ||||
| /* special eraser modes */ | |||||
| if (p->paintmode == GP_PAINTMODE_ERASER) { | |||||
| if (event->shift > 0) { | |||||
| p->flags |= GP_PAINTFLAG_HARD_ERASER; | |||||
| } | |||||
| else { | |||||
| p->flags &= ~GP_PAINTFLAG_HARD_ERASER; | |||||
| } | |||||
| if (event->ctrl > 0) { | |||||
| p->flags |= GP_PAINTFLAG_STROKE_ERASER; | |||||
| } | |||||
| else { | |||||
| p->flags &= ~GP_PAINTFLAG_STROKE_ERASER; | |||||
| } | |||||
| } | |||||
| /* special exception for start of strokes (i.e. maybe for just a dot) */ | /* special exception for start of strokes (i.e. maybe for just a dot) */ | ||||
| if (p->flags & GP_PAINTFLAG_FIRSTRUN) { | if (p->flags & GP_PAINTFLAG_FIRSTRUN) { | ||||
| p->flags &= ~GP_PAINTFLAG_FIRSTRUN; | p->flags &= ~GP_PAINTFLAG_FIRSTRUN; | ||||
| Context not available. | |||||
| RNA_float_set(&itemptr, "time", p->curtime - p->inittime); | RNA_float_set(&itemptr, "time", p->curtime - p->inittime); | ||||
| /* apply the current latest drawing point */ | /* apply the current latest drawing point */ | ||||
| gpencil_draw_apply(op, p, depsgraph); | gpencil_draw_apply(C, op, p, depsgraph); | ||||
| /* force refresh */ | /* force refresh */ | ||||
| ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ | ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ | ||||
| Context not available. | |||||
| } | } | ||||
| /* apply this data as necessary now (as per usual) */ | /* apply this data as necessary now (as per usual) */ | ||||
| gpencil_draw_apply(op, p, depsgraph); | gpencil_draw_apply(C, op, p, depsgraph); | ||||
| } | } | ||||
| RNA_END; | RNA_END; | ||||
| Context not available. | |||||
| if (G.debug & G_DEBUG) | if (G.debug & G_DEBUG) | ||||
| printf("GPencil - Starting Drawing\n"); | printf("GPencil - Starting Drawing\n"); | ||||
| /* support for tablets eraser pen */ | |||||
| if (gpencil_is_tablet_eraser_active(event)) { | |||||
| RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); | |||||
| } | |||||
| /* try to initialize context data needed while drawing */ | /* try to initialize context data needed while drawing */ | ||||
| if (!gpencil_draw_init(C, op, event)) { | if (!gpencil_draw_init(C, op, event)) { | ||||
| if (op->customdata) | if (op->customdata) | ||||
| Context not available. | |||||
| * or unintentionally if the user scrolls outside the area)... | * or unintentionally if the user scrolls outside the area)... | ||||
| */ | */ | ||||
| gpencil_draw_cursor_set(p); | gpencil_draw_cursor_set(p); | ||||
| ED_gpencil_toggle_brush_cursor(C, true, NULL); | |||||
| /* only start drawing immediately if we're allowed to do so... */ | /* only start drawing immediately if we're allowed to do so... */ | ||||
| if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { | if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { | ||||
| Context not available. | |||||
| p->status = GP_STATUS_PAINTING; | p->status = GP_STATUS_PAINTING; | ||||
| /* handle the initial drawing - i.e. for just doing a simple dot */ | /* handle the initial drawing - i.e. for just doing a simple dot */ | ||||
| gpencil_draw_apply_event(op, event, CTX_data_depsgraph(C)); | gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C)); | ||||
| op->flag |= OP_IS_MODAL_CURSOR_REGION; | op->flag |= OP_IS_MODAL_CURSOR_REGION; | ||||
| } | } | ||||
| else { | else { | ||||
| Context not available. | |||||
| op->flag |= OP_IS_MODAL_CURSOR_REGION; | op->flag |= OP_IS_MODAL_CURSOR_REGION; | ||||
| } | } | ||||
| /* enable paint mode */ | |||||
| if (p->sa->spacetype == SPACE_VIEW3D) { | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| WorkSpace *workspace = CTX_wm_workspace(C); | |||||
| if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) { | |||||
| /* Just set paintmode flag... */ | |||||
| p->gpd->flag |= GP_DATA_STROKE_PAINTMODE; | |||||
| /* disable other GP modes */ | |||||
| p->gpd->flag &= ~GP_DATA_STROKE_EDITMODE; | |||||
| p->gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; | |||||
| p->gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; | |||||
| ob->mode = OB_MODE_GPENCIL_PAINT; | |||||
| /* set workspace mode */ | |||||
| BKE_workspace_object_mode_set(workspace, scene, ob->mode); | |||||
| /* redraw mode on screen */ | |||||
| WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); | |||||
| } | |||||
| } | |||||
| WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); | ||||
| /* add a modal handler for this operator, so that we can then draw continuous strokes */ | /* add a modal handler for this operator, so that we can then draw continuous strokes */ | ||||
| WM_event_add_modal_handler(C, op); | WM_event_add_modal_handler(C, op); | ||||
| return OPERATOR_RUNNING_MODAL; | return OPERATOR_RUNNING_MODAL; | ||||
| } | } | ||||
| Context not available. | |||||
| /* XXX: watch it with the paintmode! in future, | /* XXX: watch it with the paintmode! in future, | ||||
| * it'd be nice to allow changing paint-mode when in sketching-sessions */ | * it'd be nice to allow changing paint-mode when in sketching-sessions */ | ||||
| if (gp_session_initdata(C, p)) | if (gp_session_initdata(C, op, p)) | ||||
| gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); | gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); | ||||
| if (p->status != GP_STATUS_ERROR) { | if (p->status != GP_STATUS_ERROR) { | ||||
| Context not available. | |||||
| } | } | ||||
| else if ((ELEM(event->type, p->keymodifier)) && (event->val == KM_RELEASE)) { | else if ((ELEM(event->type, p->keymodifier)) && (event->val == KM_RELEASE)) { | ||||
| /* enable continuous if release D key in mid drawing */ | /* enable continuous if release D key in mid drawing */ | ||||
| p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; | if (p->sa->spacetype != SPACE_VIEW3D) { | ||||
| p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; | |||||
| } | |||||
| } | } | ||||
| else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { | else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { | ||||
| /* Add Blank Frame | /* Add Blank Frame | ||||
| Context not available. | |||||
| /* exit painting mode (and/or end current stroke) | /* exit painting mode (and/or end current stroke) | ||||
| * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] | * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] | ||||
| */ | */ | ||||
| if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) { | /* if polyline and release shift must cancel */ | ||||
| if ((ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) || | |||||
| ((p->paintmode == GP_PAINTMODE_DRAW_POLY) && (event->shift == 0))) | |||||
| { | |||||
| /* exit() ends the current stroke before cleaning up */ | /* exit() ends the current stroke before cleaning up */ | ||||
| /* printf("\t\tGP - end of paint op + end of stroke\n"); */ | /* printf("\t\tGP - end of paint op + end of stroke\n"); */ | ||||
| /* if drawing polygon and enable on back, must move stroke */ | /* if drawing polygon and enable on back, must move stroke */ | ||||
| Context not available. | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* drawing batch cache is dirty now */ | |||||
| BKE_gpencil_batch_cache_dirty(p->gpd); | |||||
| p->gpd->flag |= GP_DATA_CACHE_IS_DIRTY; | |||||
| p->status = GP_STATUS_DONE; | p->status = GP_STATUS_DONE; | ||||
| estate = OPERATOR_FINISHED; | estate = OPERATOR_FINISHED; | ||||
| } | } | ||||
| Context not available. | |||||
| if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { | if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { | ||||
| /* handle drawing event */ | /* handle drawing event */ | ||||
| /* printf("\t\tGP - add point\n"); */ | /* printf("\t\tGP - add point\n"); */ | ||||
| gpencil_draw_apply_event(op, event, CTX_data_depsgraph(C)); | gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C)); | ||||
| /* finish painting operation if anything went wrong just now */ | /* finish painting operation if anything went wrong just now */ | ||||
| if (p->status == GP_STATUS_ERROR) { | if (p->status == GP_STATUS_ERROR) { | ||||
| Context not available. | |||||
| gpencil_draw_status_indicators(p); | gpencil_draw_status_indicators(p); | ||||
| gpencil_draw_cursor_set(p); /* cursor may have changed outside our control - T44084 */ | gpencil_draw_cursor_set(p); /* cursor may have changed outside our control - T44084 */ | ||||
| } | } | ||||
| /* process last operations before exiting */ | /* process last operations before exiting */ | ||||
| switch (estate) { | switch (estate) { | ||||
| case OPERATOR_FINISHED: | case OPERATOR_FINISHED: | ||||
| Context not available. | |||||
| /* NOTE: wait for input is enabled by default, so that all UI code can work properly without needing users to know about this */ | /* NOTE: wait for input is enabled by default, so that all UI code can work properly without needing users to know about this */ | ||||
| prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately"); | prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately"); | ||||
| RNA_def_property_flag(prop, PROP_SKIP_SAVE); | RNA_def_property_flag(prop, PROP_SKIP_SAVE); | ||||
| prop = RNA_def_boolean(ot->srna, "no_straight", false, "No Straight lines", "Disable Ctrl key for straight lines"); | |||||
| RNA_def_property_flag(prop, PROP_SKIP_SAVE); | |||||
| prop = RNA_def_boolean(ot->srna, "no_fill", false, "No Fill Areas", "Disable fill to use stroke as fill boundary"); | |||||
| RNA_def_property_flag(prop, PROP_SKIP_SAVE); | |||||
| } | } | ||||
| Context not available. | |||||