Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_utils.c
| Show All 35 Lines | |||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_blenlib.h" | #include "BLI_blenlib.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLT_translation.h" | #include "BLT_translation.h" | ||||
| #include "BLI_rand.h" | #include "BLI_rand.h" | ||||
| #include "DNA_gpencil_types.h" | #include "DNA_gpencil_types.h" | ||||
| #include "DNA_brush_types.h" | |||||
| #include "DNA_object_types.h" | #include "DNA_object_types.h" | ||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "DNA_screen_types.h" | #include "DNA_screen_types.h" | ||||
| #include "DNA_space_types.h" | #include "DNA_space_types.h" | ||||
| #include "DNA_view3d_types.h" | #include "DNA_view3d_types.h" | ||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_gpencil.h" | #include "BKE_gpencil.h" | ||||
| #include "BKE_object.h" | |||||
| #include "BKE_paint.h" | |||||
| #include "BKE_tracking.h" | #include "BKE_tracking.h" | ||||
| #include "BKE_action.h" | #include "BKE_action.h" | ||||
| #include "BKE_screen.h" | |||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "RNA_define.h" | #include "RNA_define.h" | ||||
| #include "RNA_enum_types.h" | #include "RNA_enum_types.h" | ||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| #include "UI_view2d.h" | #include "UI_view2d.h" | ||||
| #include "ED_gpencil.h" | #include "ED_gpencil.h" | ||||
| #include "ED_clip.h" | #include "ED_clip.h" | ||||
| #include "ED_view3d.h" | #include "ED_view3d.h" | ||||
| #include "ED_object.h" | |||||
| #include "ED_screen.h" | |||||
| #include "GPU_immediate.h" | |||||
| #include "GPU_immediate_util.h" | |||||
| #include "DEG_depsgraph.h" | #include "DEG_depsgraph.h" | ||||
| #include "gpencil_intern.h" | #include "gpencil_intern.h" | ||||
| /* ******************************************************** */ | /* ******************************************************** */ | ||||
| /* Context Wrangling... */ | /* Context Wrangling... */ | ||||
| /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it, | /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it, | ||||
| * when context info is not available. | * when context info is not available. | ||||
| */ | */ | ||||
| bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob, PointerRNA *ptr) | bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob, PointerRNA *ptr) | ||||
| { | { | ||||
| /* if there's an active area, check if the particular editor may | /* if there's an active area, check if the particular editor may | ||||
| * have defined any special Grease Pencil context for editing... | * have defined any special Grease Pencil context for editing... | ||||
| */ | */ | ||||
| if (sa) { | if (sa) { | ||||
| SpaceLink *sl = sa->spacedata.first; | SpaceLink *sl = sa->spacedata.first; | ||||
| switch (sa->spacetype) { | switch (sa->spacetype) { | ||||
| case SPACE_VIEW3D: /* 3D-View */ | case SPACE_VIEW3D: /* 3D-View */ | ||||
| case SPACE_BUTS: /* properties */ | |||||
| case SPACE_INFO: /* header info (needed after workspaces merge) */ | |||||
| { | { | ||||
| BLI_assert(scene && ELEM(scene->toolsettings->gpencil_src, | /* return obgpencil datablock */ | ||||
| GP_TOOL_SOURCE_SCENE, GP_TOOL_SOURCE_OBJECT)); | if (ob && ob->type == OB_GPENCIL) { | ||||
| if (scene->toolsettings->gpencil_src == GP_TOOL_SOURCE_OBJECT) { | |||||
| /* legacy behaviour for usage with old addons requiring object-linked to objects */ | |||||
| /* just in case no active/selected object... */ | |||||
| if (ob && (ob->flag & SELECT)) { | |||||
| /* for now, as long as there's an object, default to using that in 3D-View */ | |||||
| if (ptr) RNA_id_pointer_create(&ob->id, ptr); | if (ptr) RNA_id_pointer_create(&ob->id, ptr); | ||||
| return &ob->gpd; | return &ob->gpd; | ||||
| } | } | ||||
| /* else: defaults to scene... */ | |||||
| } | |||||
| else { | else { | ||||
| if (ptr) RNA_id_pointer_create(&scene->id, ptr); | return NULL; | ||||
| return &scene->gpd; | |||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case SPACE_NODE: /* Nodes Editor */ | case SPACE_NODE: /* Nodes Editor */ | ||||
| { | { | ||||
| SpaceNode *snode = (SpaceNode *)sl; | SpaceNode *snode = (SpaceNode *)sl; | ||||
| /* return the GP data for the active node block/node */ | /* return the GP data for the active node block/node */ | ||||
| if (snode && snode->nodetree) { | if (snode && snode->nodetree) { | ||||
| ▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | |||||
| /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */ | /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */ | ||||
| bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr) | bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr) | ||||
| { | { | ||||
| ID *screen_id = (ID *)CTX_wm_screen(C); | ID *screen_id = (ID *)CTX_wm_screen(C); | ||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| ScrArea *sa = CTX_wm_area(C); | ScrArea *sa = CTX_wm_area(C); | ||||
| Object *ob = CTX_data_active_object(C); | Object *ob = CTX_data_active_object(C); | ||||
| return ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, ptr); | return ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, ptr); | ||||
| } | } | ||||
| /* -------------------------------------------------------- */ | /* -------------------------------------------------------- */ | ||||
| /* Get the active Grease Pencil datablock, when context is not available */ | /* Get the active Grease Pencil datablock, when context is not available */ | ||||
| bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob) | bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | |||||
| int gp_active_brush_poll(bContext *C) | int gp_active_brush_poll(bContext *C) | ||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | ToolSettings *ts = CTX_data_tool_settings(C); | ||||
| bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); | bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); | ||||
| return (brush != NULL); | return (brush != NULL); | ||||
| } | } | ||||
| /* poll callback for checking if there is an active palette */ | |||||
| int gp_active_palette_poll(bContext *C) | |||||
| { | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| return (palette != NULL); | |||||
| } | |||||
| /* poll callback for checking if there is an active palette color */ | |||||
| int gp_active_palettecolor_poll(bContext *C) | |||||
| { | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); | |||||
| bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); | |||||
| return (palcolor != NULL); | |||||
| } | |||||
| /* ******************************************************** */ | /* ******************************************************** */ | ||||
| /* Dynamic Enums of GP Layers */ | /* Dynamic Enums of GP Layers */ | ||||
| /* NOTE: These include an option to create a new layer and use that... */ | /* NOTE: These include an option to create a new layer and use that... */ | ||||
| /* Just existing layers */ | /* Just existing layers */ | ||||
| const EnumPropertyItem *ED_gpencil_layers_enum_itemf( | const EnumPropertyItem *ED_gpencil_layers_enum_itemf( | ||||
| bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) | bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| /* sanity check */ | /* sanity check */ | ||||
| if (ELEM(NULL, sa, gps)) | if (ELEM(NULL, sa, gps)) | ||||
| return false; | return false; | ||||
| /* filter stroke types by flags + spacetype */ | /* filter stroke types by flags + spacetype */ | ||||
| if (gps->flag & GP_STROKE_3DSPACE) { | if (gps->flag & GP_STROKE_3DSPACE) { | ||||
| /* 3D strokes - only in 3D view */ | /* 3D strokes - only in 3D view */ | ||||
| return (sa->spacetype == SPACE_VIEW3D); | return ((sa->spacetype == SPACE_VIEW3D) || (sa->spacetype == SPACE_BUTS)); | ||||
| } | } | ||||
| else if (gps->flag & GP_STROKE_2DIMAGE) { | else if (gps->flag & GP_STROKE_2DIMAGE) { | ||||
| /* Special "image" strokes - only in Image Editor */ | /* Special "image" strokes - only in Image Editor */ | ||||
| return (sa->spacetype == SPACE_IMAGE); | return (sa->spacetype == SPACE_IMAGE); | ||||
| } | } | ||||
| else if (gps->flag & GP_STROKE_2DSPACE) { | else if (gps->flag & GP_STROKE_2DSPACE) { | ||||
| /* 2D strokes (dataspace) - for any 2D view (i.e. everything other than 3D view) */ | /* 2D strokes (dataspace) - for any 2D view (i.e. everything other than 3D view) */ | ||||
| return (sa->spacetype != SPACE_VIEW3D); | return (sa->spacetype != SPACE_VIEW3D); | ||||
| Show All 10 Lines | bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) | ||||
| ScrArea *sa = CTX_wm_area(C); | ScrArea *sa = CTX_wm_area(C); | ||||
| return ED_gpencil_stroke_can_use_direct(sa, gps); | return ED_gpencil_stroke_can_use_direct(sa, gps); | ||||
| } | } | ||||
| /* Check whether given stroke can be edited for the current color */ | /* Check whether given stroke can be edited for the current color */ | ||||
| bool ED_gpencil_stroke_color_use(const bGPDlayer *gpl, const bGPDstroke *gps) | bool ED_gpencil_stroke_color_use(const bGPDlayer *gpl, const bGPDstroke *gps) | ||||
| { | { | ||||
| /* check if the color is editable */ | /* check if the color is editable */ | ||||
| bGPDpalettecolor *palcolor = gps->palcolor; | PaletteColor *palcolor = gps->palcolor; | ||||
| if (palcolor != NULL) { | if ((gps->palette) && (palcolor != NULL)) { | ||||
| if (palcolor->flag & PC_COLOR_HIDE) | if (palcolor->flag & PC_COLOR_HIDE) | ||||
| return false; | return false; | ||||
| if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) | if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) | ||||
| return false; | return false; | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| /* Get palette color or create a new one */ | |||||
| bGPDpalettecolor *ED_gpencil_stroke_getcolor(bGPdata *gpd, bGPDstroke *gps) | |||||
| { | |||||
| bGPDpalette *palette; | |||||
| bGPDpalettecolor *palcolor; | |||||
| if ((gps->palcolor != NULL) && ((gps->flag & GP_STROKE_RECALC_COLOR) == 0)) | |||||
| return gps->palcolor; | |||||
| /* get palette */ | |||||
| palette = BKE_gpencil_palette_getactive(gpd); | |||||
| if (palette == NULL) { | |||||
| palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); | |||||
| } | |||||
| /* get color */ | |||||
| palcolor = BKE_gpencil_palettecolor_getbyname(palette, gps->colorname); | |||||
| if (palcolor == NULL) { | |||||
| if (gps->palcolor == NULL) { | |||||
| palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); | |||||
| /* set to a different color */ | |||||
| ARRAY_SET_ITEMS(palcolor->color, 1.0f, 0.0f, 1.0f, 0.9f); | |||||
| } | |||||
| else { | |||||
| palcolor = BKE_gpencil_palettecolor_addnew(palette, gps->colorname, true); | |||||
| /* set old color and attributes */ | |||||
| bGPDpalettecolor *gpscolor = gps->palcolor; | |||||
| copy_v4_v4(palcolor->color, gpscolor->color); | |||||
| copy_v4_v4(palcolor->fill, gpscolor->fill); | |||||
| palcolor->flag = gpscolor->flag; | |||||
| } | |||||
| } | |||||
| /* clear flag and set pointer */ | |||||
| gps->flag &= ~GP_STROKE_RECALC_COLOR; | |||||
| gps->palcolor = palcolor; | |||||
| return palcolor; | |||||
| } | |||||
| /* ******************************************************** */ | /* ******************************************************** */ | ||||
| /* Space Conversion */ | /* Space Conversion */ | ||||
| /** | /** | ||||
| * Init settings for stroke point space conversions | * Init settings for stroke point space conversions | ||||
| * | * | ||||
| * \param r_gsc: [out] The space conversion settings struct, populated with necessary params | * \param r_gsc: [out] The space conversion settings struct, populated with necessary params | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| float fpt[3]; | float fpt[3]; | ||||
| mul_v3_m4v3(fpt, diff_mat, &pt->x); | mul_v3_m4v3(fpt, diff_mat, &pt->x); | ||||
| copy_v3_v3(&r_pt->x, fpt); | copy_v3_v3(&r_pt->x, fpt); | ||||
| } | } | ||||
| /** | /** | ||||
| * Change points position relative to parent object | * Change position relative to parent object | ||||
| */ | */ | ||||
| void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps) | void gp_apply_parent(Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps) | ||||
| { | { | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| int i; | int i; | ||||
| /* undo matrix */ | /* undo matrix */ | ||||
| float diff_mat[4][4]; | float diff_mat[4][4]; | ||||
| float inverse_diff_mat[4][4]; | float inverse_diff_mat[4][4]; | ||||
| float fpt[3]; | float fpt[3]; | ||||
| ED_gpencil_parent_location(gpl, diff_mat); | ED_gpencil_parent_location(obact, gpd, gpl, diff_mat); | ||||
| invert_m4_m4(inverse_diff_mat, diff_mat); | invert_m4_m4(inverse_diff_mat, diff_mat); | ||||
| for (i = 0; i < gps->totpoints; i++) { | for (i = 0; i < gps->totpoints; i++) { | ||||
| pt = &gps->points[i]; | pt = &gps->points[i]; | ||||
| mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); | mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); | ||||
| copy_v3_v3(&pt->x, fpt); | copy_v3_v3(&pt->x, fpt); | ||||
| } | } | ||||
| } | } | ||||
| /** | /** | ||||
| * Change point position relative to parent object | * Change point position relative to parent object | ||||
| */ | */ | ||||
| void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt) | void gp_apply_parent_point(Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt) | ||||
| { | { | ||||
| /* undo matrix */ | /* undo matrix */ | ||||
| float diff_mat[4][4]; | float diff_mat[4][4]; | ||||
| float inverse_diff_mat[4][4]; | float inverse_diff_mat[4][4]; | ||||
| float fpt[3]; | float fpt[3]; | ||||
| ED_gpencil_parent_location(gpl, diff_mat); | ED_gpencil_parent_location(obact, gpd, gpl, diff_mat); | ||||
| invert_m4_m4(inverse_diff_mat, diff_mat); | invert_m4_m4(inverse_diff_mat, diff_mat); | ||||
| mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); | mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); | ||||
| copy_v3_v3(&pt->x, fpt); | copy_v3_v3(&pt->x, fpt); | ||||
| } | } | ||||
| /** | /** | ||||
| * Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D) | * Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D) | ||||
| ▲ Show 20 Lines • Show All 392 Lines • ▼ Show 20 Lines | for (int i = 1; i < gps->totpoints - 1; ++i) { | ||||
| } | } | ||||
| /* apply shift */ | /* apply shift */ | ||||
| add_v3_v3(&pt->x, svec); | add_v3_v3(&pt->x, svec); | ||||
| } | } | ||||
| } | } | ||||
| /* calculate difference matrix */ | /* calculate difference matrix */ | ||||
| void ED_gpencil_parent_location(bGPDlayer *gpl, float diff_mat[4][4]) | void ED_gpencil_parent_location(Object *obact, bGPdata *gpd, bGPDlayer *gpl, float diff_mat[4][4]) | ||||
| { | { | ||||
| Object *ob = gpl->parent; | Object *obparent = gpl->parent; | ||||
| if (ob == NULL) { | /* if not layer parented, try with object parented */ | ||||
| if (obparent == NULL) { | |||||
| if (obact != NULL) { | |||||
| /* the gpd can be scene, but a gpobject can be active, so need check gpd */ | |||||
| if ((obact->type == OB_GPENCIL) && (obact->gpd == gpd)) { | |||||
| copy_m4_m4(diff_mat, obact->obmat); | |||||
| return; | |||||
| } | |||||
| } | |||||
| /* not gpencil object */ | |||||
| unit_m4(diff_mat); | unit_m4(diff_mat); | ||||
| return; | return; | ||||
| } | } | ||||
| else { | else { | ||||
| if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { | if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { | ||||
| mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); | mul_m4_m4m4(diff_mat, obparent->obmat, gpl->inverse); | ||||
| return; | return; | ||||
| } | } | ||||
| else if (gpl->partype == PARBONE) { | else if (gpl->partype == PARBONE) { | ||||
| bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, gpl->parsubstr); | bPoseChannel *pchan = BKE_pose_channel_find_name(obparent->pose, gpl->parsubstr); | ||||
| if (pchan) { | if (pchan) { | ||||
| float tmp_mat[4][4]; | float tmp_mat[4][4]; | ||||
| mul_m4_m4m4(tmp_mat, ob->obmat, pchan->pose_mat); | mul_m4_m4m4(tmp_mat, obparent->obmat, pchan->pose_mat); | ||||
| mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse); | mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse); | ||||
| } | } | ||||
| else { | else { | ||||
| mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); /* if bone not found use object (armature) */ | mul_m4_m4m4(diff_mat, obparent->obmat, gpl->inverse); /* if bone not found use object (armature) */ | ||||
| } | } | ||||
| return; | return; | ||||
| } | } | ||||
| else { | else { | ||||
| unit_m4(diff_mat); /* not defined type */ | unit_m4(diff_mat); /* not defined type */ | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* reset parent matrix for all layers */ | /* reset parent matrix for all layers */ | ||||
| void ED_gpencil_reset_layers_parent(bGPdata *gpd) | void ED_gpencil_reset_layers_parent(Object *obact, bGPdata *gpd) | ||||
| { | { | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| int i; | int i; | ||||
| float diff_mat[4][4]; | float diff_mat[4][4]; | ||||
| float cur_mat[4][4]; | float cur_mat[4][4]; | ||||
| for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| if (gpl->parent != NULL) { | if (gpl->parent != NULL) { | ||||
| /* calculate new matrix */ | /* calculate new matrix */ | ||||
| if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { | if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { | ||||
| invert_m4_m4(cur_mat, gpl->parent->obmat); | invert_m4_m4(cur_mat, gpl->parent->obmat); | ||||
| } | } | ||||
| else if (gpl->partype == PARBONE) { | else if (gpl->partype == PARBONE) { | ||||
| bPoseChannel *pchan = BKE_pose_channel_find_name(gpl->parent->pose, gpl->parsubstr); | bPoseChannel *pchan = BKE_pose_channel_find_name(gpl->parent->pose, gpl->parsubstr); | ||||
| if (pchan) { | if (pchan) { | ||||
| float tmp_mat[4][4]; | float tmp_mat[4][4]; | ||||
| mul_m4_m4m4(tmp_mat, gpl->parent->obmat, pchan->pose_mat); | mul_m4_m4m4(tmp_mat, gpl->parent->obmat, pchan->pose_mat); | ||||
| invert_m4_m4(cur_mat, tmp_mat); | invert_m4_m4(cur_mat, tmp_mat); | ||||
| } | } | ||||
| } | } | ||||
| /* only redo if any change */ | /* only redo if any change */ | ||||
| if (!equals_m4m4(gpl->inverse, cur_mat)) { | if (!equals_m4m4(gpl->inverse, cur_mat)) { | ||||
| /* first apply current transformation to all strokes */ | /* first apply current transformation to all strokes */ | ||||
| ED_gpencil_parent_location(gpl, diff_mat); | ED_gpencil_parent_location(obact, gpd, gpl, diff_mat); | ||||
| for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | ||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | ||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | ||||
| mul_m4_v3(diff_mat, &pt->x); | mul_m4_v3(diff_mat, &pt->x); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* set new parent matrix */ | /* set new parent matrix */ | ||||
| ▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | for (palette = gpd->palettes.first; palette; palette = palette->next, i++) { | ||||
| RNA_enum_item_add(&item, &totitem, &item_tmp); | RNA_enum_item_add(&item, &totitem, &item_tmp); | ||||
| } | } | ||||
| RNA_enum_item_end(&item, &totitem); | RNA_enum_item_end(&item, &totitem); | ||||
| *r_free = true; | *r_free = true; | ||||
| return item; | return item; | ||||
| } | } | ||||
| /* Helper function to create new OB_GPENCIL Object */ | |||||
| Object *ED_add_gpencil_object(bContext *C, Scene *scene, const float loc[3]) | |||||
| { | |||||
| float rot[3]; | |||||
| zero_v3(rot); | |||||
| Object *ob = ED_object_add_type(C, OB_GPENCIL, NULL, loc, rot, false, scene->lay); | |||||
| /* define size */ | |||||
| BKE_object_obdata_size_init(ob, GP_OBGPENCIL_DEFAULT_SIZE); | |||||
| /* create default brushes and colors */ | |||||
| ED_gpencil_add_defaults(C); | |||||
| return ob; | |||||
| } | |||||
| /* Helper function to create default colors and drawing brushes */ | |||||
| void ED_gpencil_add_defaults(bContext *C) | |||||
| { | |||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| bGPdata *gpd = CTX_data_gpencil_data(C); | |||||
| /* ensure palettes, colors, and palette slots exist */ | |||||
| BKE_gpencil_paletteslot_validate(CTX_data_main(C), gpd); | |||||
| /* create default brushes */ | |||||
| if (BLI_listbase_is_empty(&ts->gp_brushes)) { | |||||
| BKE_gpencil_brush_init_presets(ts); | |||||
| } | |||||
| } | |||||
| /* allocate memory for saving gp object to be sorted by zdepth */ | |||||
| tGPencilSort *ED_gpencil_allocate_cache(tGPencilSort *cache, int *gp_cache_size, int gp_cache_used) | |||||
| { | |||||
| tGPencilSort *p = NULL; | |||||
| /* By default a cache is created with one block with a predefined number of free slots, | |||||
| if the size is not enough, the cache is reallocated adding a new block of free slots. | |||||
| This is done in order to keep cache small */ | |||||
| if (gp_cache_used + 1 > *gp_cache_size) { | |||||
| if ((*gp_cache_size == 0) || (cache == NULL)) { | |||||
| p = MEM_callocN(sizeof(struct tGPencilSort) * GP_CACHE_BLOCK_SIZE, "tGPencilSort"); | |||||
| *gp_cache_size = GP_CACHE_BLOCK_SIZE; | |||||
| } | |||||
| else { | |||||
| *gp_cache_size += GP_CACHE_BLOCK_SIZE; | |||||
| p = MEM_recallocN(cache, sizeof(struct tGPencilSort) * *gp_cache_size); | |||||
| } | |||||
| cache = p; | |||||
| } | |||||
| return cache; | |||||
| } | |||||
| /* add gp object to the temporary cache for sorting */ | |||||
| void ED_gpencil_add_to_cache(tGPencilSort *cache, RegionView3D *rv3d, Base *base, int *gp_cache_used) | |||||
| { | |||||
| /* save object */ | |||||
| cache[*gp_cache_used].base = base; | |||||
| /* calculate zdepth from point of view */ | |||||
| float zdepth = 0.0; | |||||
| if (rv3d->is_persp) { | |||||
| zdepth = ED_view3d_calc_zfac(rv3d, base->object->loc, NULL); | |||||
| } | |||||
| else { | |||||
| zdepth = -dot_v3v3(rv3d->viewinv[2], base->object->loc); | |||||
| } | |||||
| cache[*gp_cache_used].zdepth = zdepth; | |||||
| /* increase slots used in cache */ | |||||
| (*gp_cache_used)++; | |||||
| } | |||||
| /* reproject the points of the stroke to a plane locked to axis to avoid stroke offset */ | |||||
| void ED_gp_project_stroke_to_plane(Object *ob, RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis, char type) | |||||
| { | |||||
| 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; | |||||
| /* if object, apply object rotation */ | |||||
| if (type & GP_TOOL_SOURCE_OBJECT) { | |||||
| if (ob && ob->type == OB_GPENCIL) { | |||||
| mul_mat3_m4_v3(ob->obmat, plane_normal); | |||||
| } | |||||
| } | |||||
| /* 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 one points to a plane locked to axis to avoid stroke offset */ | |||||
| void ED_gp_project_point_to_plane(Object *ob, RegionView3D *rv3d, const float origin[3], const int axis, char type, bGPDspoint *pt) | |||||
| { | |||||
| float plane_normal[3]; | |||||
| float vn[3]; | |||||
| float ray[3]; | |||||
| float rpoint[3]; | |||||
| /* no need reproject */ | |||||
| if (axis < 0) { | |||||
| return; | |||||
| } | |||||
| /* normal vector for a plane locked to axis */ | |||||
| zero_v3(plane_normal); | |||||
| plane_normal[axis] = 1.0f; | |||||
| /* if object, apply object rotation */ | |||||
| if (type & GP_TOOL_SOURCE_OBJECT) { | |||||
| if (ob && ob->type == OB_GPENCIL) { | |||||
| mul_mat3_m4_v3(ob->obmat, plane_normal); | |||||
| } | |||||
| } | |||||
| /* Reproject the points in the plane */ | |||||
| /* 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); | |||||
| } | |||||
| } | |||||
| /* get drawing reference for conversion or projection of the stroke */ | |||||
| void ED_gp_get_drawing_reference(ToolSettings *ts, View3D *v3d, Scene *scene, Object *ob, bGPDlayer *gpl, char align_flag, float vec[3]) | |||||
| { | |||||
| const float *fp = ED_view3d_cursor3d_get(scene, v3d); | |||||
| /* if using a gpencil object at cursor mode, can use the location of the object */ | |||||
| if ((ts->gpencil_src & GP_TOOL_SOURCE_OBJECT) && (align_flag & GP_PROJECT_VIEWSPACE)) { | |||||
| if (ob) { | |||||
| if (ob->type == OB_GPENCIL) { | |||||
| /* use last stroke position for layer */ | |||||
| if (gpl && gpl->flag & GP_LAYER_USE_LOCATION) { | |||||
| if (gpl->actframe) { | |||||
| bGPDframe *gpf = gpl->actframe; | |||||
| if (gpf->strokes.last) { | |||||
| bGPDstroke *gps = gpf->strokes.last; | |||||
| if (gps->totpoints > 0) { | |||||
| copy_v3_v3(vec, &gps->points[gps->totpoints - 1].x); | |||||
| mul_m4_v3(ob->obmat, vec); | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| /* use cursor */ | |||||
| if (align_flag & GP_PROJECT_CURSOR) { | |||||
| /* use 3D-cursor */ | |||||
| copy_v3_v3(vec, fp); | |||||
| } | |||||
| else { | |||||
| /* use object location */ | |||||
| copy_v3_v3(vec, ob->obmat[3]); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* use 3D-cursor */ | |||||
| copy_v3_v3(vec, fp); | |||||
| } | |||||
| } | |||||
| /* ******************************************************** */ | /* ******************************************************** */ | ||||
| /* Cursor drawing */ | |||||
| /* check if cursor is in drawing region */ | |||||
| static bool gp_check_cursor_region(bContext *C, int mval[2]) | |||||
| { | |||||
| ARegion *ar = CTX_wm_region(C); | |||||
| ScrArea *sa = CTX_wm_area(C); | |||||
| /* TODO: add more spacetypes */ | |||||
| if (!ELEM(sa->spacetype, SPACE_VIEW3D)) { | |||||
| return false; | |||||
| } | |||||
| if ((ar) && (ar->regiontype != RGN_TYPE_WINDOW)) { | |||||
| return false; | |||||
| } | |||||
| else if (ar) { | |||||
| rcti region_rect; | |||||
| /* Perform bounds check using */ | |||||
| ED_region_visible_rect(ar, ®ion_rect); | |||||
| return BLI_rcti_isect_pt_v(®ion_rect, mval); | |||||
| } | |||||
| else { | |||||
| return false; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| /* Helper callback for drawing the cursor itself */ | |||||
| static void gp_brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata)) | |||||
| { | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| GP_EditBrush_Data *brush = NULL; | |||||
| if ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) { | |||||
| brush = &gset->brush[gset->weighttype]; | |||||
| } | |||||
| else { | |||||
| brush = &gset->brush[gset->brushtype]; | |||||
| } | |||||
| bGPDbrush *paintbrush; | |||||
| /* default radius and color */ | |||||
| float radius = 5.0f; | |||||
| float color[3], darkcolor[3]; | |||||
| ARRAY_SET_ITEMS(color, 1.0f, 1.0f, 1.0f); | |||||
| int mval[2]; | |||||
| ARRAY_SET_ITEMS(mval, x, y); | |||||
| /* check if cursor is in drawing region and has valid datablock */ | |||||
| if ((!gp_check_cursor_region(C, mval)) || (gpd == NULL)) { | |||||
| return; | |||||
| } | |||||
| /* for paint use paint brush size and color */ | |||||
| if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { | |||||
| /* while drawing hide */ | |||||
| if (gpd->sbuffer_size > 0) { | |||||
| return; | |||||
| } | |||||
| paintbrush = BKE_gpencil_brush_getactive(scene->toolsettings); | |||||
| if (paintbrush) { | |||||
| if ((paintbrush->flag & GP_BRUSH_ENABLE_CURSOR) == 0) { | |||||
| return; | |||||
| } | |||||
| /* after some testing, display the size of the brush is not practical because | |||||
| * is too disruptive and the size of cursor does not change with zoom factor. | |||||
| * The decision was to use a fix size, instead of paintbrush->thickness value. | |||||
| */ | |||||
| radius = 3.0f; | |||||
| copy_v3_v3(color, paintbrush->curcolor); | |||||
| } | |||||
| } | |||||
| /* for sculpt use sculpt brush size */ | |||||
| if (GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd)) { | |||||
| if (brush) { | |||||
| if ((brush->flag & GP_EDITBRUSH_FLAG_ENABLE_CURSOR) == 0) { | |||||
| return; | |||||
| } | |||||
| radius = brush->size; | |||||
| if (brush->flag & (GP_EDITBRUSH_FLAG_INVERT | GP_EDITBRUSH_FLAG_TMP_INVERT)) { | |||||
| copy_v3_v3(color, brush->curcolor_sub); | |||||
| } | |||||
| else { | |||||
| copy_v3_v3(color, brush->curcolor_add); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* draw icon */ | |||||
| Gwn_VertFormat *format = immVertexFormat(); | |||||
| unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); | |||||
| immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); | |||||
| glEnable(GL_LINE_SMOOTH); | |||||
| glEnable(GL_BLEND); | |||||
| /* Inner Ring: Color from UI panel */ | |||||
| immUniformColor4f(color[0], color[1], color[2], 0.8f); | |||||
| imm_draw_circle_wire_2d(pos, x, y, radius, 40); | |||||
| /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ | |||||
| mul_v3_v3fl(darkcolor, color, 0.40f); | |||||
| immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f); | |||||
| imm_draw_circle_wire_2d(pos, x, y, radius + 1, 40); | |||||
| immUnbindProgram(); | |||||
| glDisable(GL_BLEND); | |||||
| glDisable(GL_LINE_SMOOTH); | |||||
| } | |||||
| /* Turn brush cursor in on/off */ | |||||
| void ED_gpencil_toggle_brush_cursor(bContext *C, bool enable) | |||||
| { | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; | |||||
| if (gset->paintcursor && !enable) { | |||||
| /* clear cursor */ | |||||
| WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); | |||||
| gset->paintcursor = NULL; | |||||
| } | |||||
| else if (enable) { | |||||
| /* in some situations cursor could be duplicated, so it is better disable first if exist */ | |||||
| if (gset->paintcursor) { | |||||
| /* clear cursor */ | |||||
| WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); | |||||
| gset->paintcursor = NULL; | |||||
| } | |||||
| /* enable cursor */ | |||||
| gset->paintcursor = WM_paint_cursor_activate(CTX_wm_manager(C), | |||||
| NULL, | |||||
| gp_brush_drawcursor, NULL); | |||||
| } | |||||
| } | |||||
| /* assign points to vertex group */ | |||||
| void ED_gpencil_vgroup_assign(bContext *C, Object *ob, float weight) | |||||
| { | |||||
| bGPDspoint *pt; | |||||
| const int def_nr = ob->actdef - 1; | |||||
| if (!BLI_findlink(&ob->defbase, def_nr)) | |||||
| return; | |||||
| CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | |||||
| { | |||||
| if (gps->flag & GP_STROKE_SELECT) { | |||||
| for (int i = 0; i < gps->totpoints; ++i) { | |||||
| pt = &gps->points[i]; | |||||
| if (pt->flag & GP_SPOINT_SELECT) { | |||||
| BKE_gpencil_vgroup_add_point_weight(pt, def_nr, weight); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| } | |||||
| /* remove points from vertex group */ | |||||
| void ED_gpencil_vgroup_remove(bContext *C, Object *ob) | |||||
| { | |||||
| bGPDspoint *pt; | |||||
| const int def_nr = ob->actdef - 1; | |||||
| if (!BLI_findlink(&ob->defbase, def_nr)) | |||||
| return; | |||||
| CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | |||||
| { | |||||
| for (int i = 0; i < gps->totpoints; ++i) { | |||||
| pt = &gps->points[i]; | |||||
| if ((pt->flag & GP_SPOINT_SELECT) && (pt->totweight > 0)) { | |||||
| BKE_gpencil_vgroup_remove_point_weight(pt, def_nr); | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| } | |||||
| /* select points of vertex group */ | |||||
| void ED_gpencil_vgroup_select(bContext *C, Object *ob) | |||||
| { | |||||
| bGPDspoint *pt; | |||||
| const int def_nr = ob->actdef - 1; | |||||
| if (!BLI_findlink(&ob->defbase, def_nr)) | |||||
| return; | |||||
| CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | |||||
| { | |||||
| for (int i = 0; i < gps->totpoints; ++i) { | |||||
| pt = &gps->points[i]; | |||||
| if (BKE_gpencil_vgroup_use_index(pt, def_nr) > -1.0f) { | |||||
| pt->flag |= GP_SPOINT_SELECT; | |||||
| gps->flag |= GP_STROKE_SELECT; | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| } | |||||
| /* unselect points of vertex group */ | |||||
| void ED_gpencil_vgroup_deselect(bContext *C, Object *ob) | |||||
| { | |||||
| bGPDspoint *pt; | |||||
| const int def_nr = ob->actdef - 1; | |||||
| if (!BLI_findlink(&ob->defbase, def_nr)) | |||||
| return; | |||||
| CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | |||||
| { | |||||
| for (int i = 0; i < gps->totpoints; ++i) { | |||||
| pt = &gps->points[i]; | |||||
| if (BKE_gpencil_vgroup_use_index(pt, def_nr) > -1.0f) { | |||||
| pt->flag &= ~GP_SPOINT_SELECT; | |||||
| gps->flag |= GP_STROKE_SELECT; | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| } | |||||