Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_utils.c
| Show All 34 Lines | |||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #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_meshdata_types.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_action.h" | #include "BKE_action.h" | ||||
| #include "BKE_main.h" | |||||
| #include "BKE_brush.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_gpencil.h" | #include "BKE_gpencil.h" | ||||
| #include "BKE_main.h" | #include "BKE_object.h" | ||||
| #include "BKE_paint.h" | |||||
| #include "BKE_material.h" | |||||
| #include "BKE_tracking.h" | #include "BKE_tracking.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 "DEG_depsgraph_query.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, ScrArea *sa, Object *ob, PointerRNA *r_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 */ | ||||
| BLI_assert(scene && ELEM(scene->toolsettings->gpencil_src, | case SPACE_INFO: /* header info (needed after workspaces merge) */ | ||||
| GP_TOOL_SOURCE_SCENE, GP_TOOL_SOURCE_OBJECT)); | case SPACE_TOPBAR: /* Topbar (needed after topbar merge) */ | ||||
| { | |||||
| if (scene->toolsettings->gpencil_src == GP_TOOL_SOURCE_OBJECT) { | /* return obgpencil datablock */ | ||||
| /* legacy behaviour for usage with old addons requiring object-linked to objects */ | if (ob && (ob->type == OB_GPENCIL)) { | ||||
| if (r_ptr) RNA_id_pointer_create(&ob->id, r_ptr); | |||||
| /* just in case no active/selected object... */ | return (bGPdata **)&ob->data; | ||||
| 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); | |||||
| 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) { | ||||
| /* for now, as long as there's an active node tree, default to using that in the Nodes Editor */ | /* for now, as long as there's an active node tree, default to using that in the Nodes Editor */ | ||||
| if (ptr) RNA_id_pointer_create(&snode->nodetree->id, ptr); | if (r_ptr) RNA_id_pointer_create(&snode->nodetree->id, r_ptr); | ||||
| return &snode->nodetree->gpd; | return &snode->nodetree->gpd; | ||||
| } | } | ||||
| /* even when there is no node-tree, don't allow this to flow to scene */ | /* even when there is no node-tree, don't allow this to flow to scene */ | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| case SPACE_SEQ: /* Sequencer */ | case SPACE_SEQ: /* Sequencer */ | ||||
| { | { | ||||
| SpaceSeq *sseq = (SpaceSeq *)sl; | SpaceSeq *sseq = (SpaceSeq *)sl; | ||||
| /* for now, Grease Pencil data is associated with the space (actually preview region only) */ | /* for now, Grease Pencil data is associated with the space (actually preview region only) */ | ||||
| /* XXX our convention for everything else is to link to data though... */ | /* XXX our convention for everything else is to link to data though... */ | ||||
| if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, ptr); | if (r_ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, r_ptr); | ||||
| return &sseq->gpd; | return &sseq->gpd; | ||||
| } | } | ||||
| case SPACE_IMAGE: /* Image/UV Editor */ | case SPACE_IMAGE: /* Image/UV Editor */ | ||||
| { | { | ||||
| SpaceImage *sima = (SpaceImage *)sl; | SpaceImage *sima = (SpaceImage *)sl; | ||||
| /* for now, Grease Pencil data is associated with the space... */ | /* for now, Grease Pencil data is associated with the space... */ | ||||
| /* XXX our convention for everything else is to link to data though... */ | /* XXX our convention for everything else is to link to data though... */ | ||||
| if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, ptr); | if (r_ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, r_ptr); | ||||
| return &sima->gpd; | return &sima->gpd; | ||||
| } | } | ||||
| case SPACE_CLIP: /* Nodes Editor */ | case SPACE_CLIP: /* Nodes Editor */ | ||||
| { | { | ||||
| SpaceClip *sc = (SpaceClip *)sl; | SpaceClip *sc = (SpaceClip *)sl; | ||||
| MovieClip *clip = ED_space_clip_get_clip(sc); | MovieClip *clip = ED_space_clip_get_clip(sc); | ||||
| if (clip) { | if (clip) { | ||||
| if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { | if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { | ||||
| MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); | MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); | ||||
| if (!track) | if (!track) | ||||
| return NULL; | return NULL; | ||||
| if (ptr) | if (r_ptr) RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, r_ptr); | ||||
| RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, ptr); | |||||
| return &track->gpd; | return &track->gpd; | ||||
| } | } | ||||
| else { | else { | ||||
| if (ptr) | if (r_ptr) RNA_id_pointer_create(&clip->id, r_ptr); | ||||
| RNA_id_pointer_create(&clip->id, ptr); | |||||
| return &clip->gpd; | return &clip->gpd; | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| default: /* unsupported space */ | default: /* unsupported space */ | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| } | } | ||||
| /* just fall back on the scene's GP data */ | return NULL; | ||||
| if (ptr) RNA_id_pointer_create((ID *)scene, ptr); | |||||
| return (scene) ? &scene->gpd : NULL; | |||||
| } | } | ||||
| /* 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 *r_ptr) | ||||
| { | { | ||||
| ID *screen_id = (ID *)CTX_wm_screen(C); | ID *screen_id = (ID *)CTX_wm_screen(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, sa, ob, r_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, ScrArea *sa, Object *ob) | ||||
| { | { | ||||
| bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, NULL); | bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, sa, ob, NULL); | ||||
| return (gpd_ptr) ? *(gpd_ptr) : NULL; | return (gpd_ptr) ? *(gpd_ptr) : NULL; | ||||
| } | } | ||||
| /* Get the active Grease Pencil datablock */ | /** | ||||
| * Get the active Grease Pencil datablock | |||||
| * \note This is the original (bmain) copy of the datablock, stored in files. | |||||
| * Do not use for reading evaluated copies of GP Objects data | |||||
| */ | |||||
| bGPdata *ED_gpencil_data_get_active(const bContext *C) | bGPdata *ED_gpencil_data_get_active(const bContext *C) | ||||
| { | { | ||||
| bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); | ||||
| return (gpd_ptr) ? *(gpd_ptr) : NULL; | return (gpd_ptr) ? *(gpd_ptr) : NULL; | ||||
| } | } | ||||
| /** | |||||
| * Get the evaluated copy of the active Grease Pencil datablock (where applicable) | |||||
| * - For the 3D View (i.e. "GP Objects"), this gives the evaluated copy of the GP datablock | |||||
| * (i.e. a copy of the active GP datablock for the active object, where modifiers have been | |||||
| * applied). This is needed to correctly work with "Copy-on-Write" | |||||
| * - For all other editors (i.e. "GP Annotations"), this just gives the active datablock | |||||
| * like for ED_gpencil_data_get_active() | |||||
| */ | |||||
| bGPdata *ED_gpencil_data_get_active_evaluated(const bContext *C) | |||||
| { | |||||
| ID *screen_id = (ID *)CTX_wm_screen(C); | |||||
| ScrArea *sa = CTX_wm_area(C); | |||||
| const Depsgraph *depsgraph = CTX_data_depsgraph(C); | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); | |||||
| /* if (ob && ob->type == OB_GPENCIL) BLI_assert(ob_eval->data == DEG_get_evaluated_id(ob->data)); */ | |||||
| return ED_gpencil_data_get_active_direct(screen_id, sa, ob_eval); | |||||
| } | |||||
| /* -------------------------------------------------------- */ | /* -------------------------------------------------------- */ | ||||
| // XXX: this should be removed... We really shouldn't duplicate logic like this! | // XXX: this should be removed... We really shouldn't duplicate logic like this! | ||||
| bGPdata *ED_gpencil_data_get_active_v3d(Scene *scene, ViewLayer *view_layer) | bGPdata *ED_gpencil_data_get_active_v3d(ViewLayer *view_layer) | ||||
| { | { | ||||
| Base *base = view_layer->basact; | Base *base = view_layer->basact; | ||||
| bGPdata *gpd = NULL; | bGPdata *gpd = NULL; | ||||
| /* We have to make sure active object is actually visible and selected, else we must use default scene gpd, | /* We have to make sure active object is actually visible and selected, else we must use default scene gpd, | ||||
| * to be consistent with ED_gpencil_data_get_active's behavior. | * to be consistent with ED_gpencil_data_get_active's behavior. | ||||
| */ | */ | ||||
| if (base && TESTBASE(base)) { | if (base && TESTBASE(base)) { | ||||
| if (base->object->type == OB_GPENCIL) | |||||
| gpd = base->object->data; | |||||
| else | |||||
| gpd = base->object->gpd; | gpd = base->object->gpd; | ||||
| } | } | ||||
| return gpd ? gpd : scene->gpd; | return gpd ? gpd : NULL; | ||||
| } | } | ||||
| /* ******************************************************** */ | /* ******************************************************** */ | ||||
| /* Keyframe Indicator Checks */ | /* Keyframe Indicator Checks */ | ||||
| /* Check whether there's an active GP keyframe on the current frame */ | /* Check whether there's an active GP keyframe on the current frame */ | ||||
| bool ED_gpencil_has_keyframe_v3d(Scene *scene, Object *ob, int cfra) | bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra) | ||||
| { | { | ||||
| /* just check both for now... */ | if (ob && ob->data && (ob->type == OB_GPENCIL)) { | ||||
| // XXX: this could get confusing (e.g. if only on the object, but other places don't show this) | bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->data); | ||||
| if (scene->gpd) { | |||||
| bGPDlayer *gpl = BKE_gpencil_layer_getactive(scene->gpd); | |||||
| if (gpl) { | |||||
| if (gpl->actframe) { | |||||
| // XXX: assumes that frame has been fetched already | |||||
| return (gpl->actframe->framenum == cfra); | |||||
| } | |||||
| else { | |||||
| /* XXX: disabled as could be too much of a penalty */ | |||||
| /* return BKE_gpencil_layer_find_frame(gpl, cfra); */ | |||||
| } | |||||
| } | |||||
| } | |||||
| if (ob && ob->gpd) { | |||||
| bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->gpd); | |||||
| if (gpl) { | if (gpl) { | ||||
| if (gpl->actframe) { | if (gpl->actframe) { | ||||
| // XXX: assumes that frame has been fetched already | // XXX: assumes that frame has been fetched already | ||||
| return (gpl->actframe->framenum == cfra); | return (gpl->actframe->framenum == cfra); | ||||
| } | } | ||||
| else { | else { | ||||
| /* XXX: disabled as could be too much of a penalty */ | /* XXX: disabled as could be too much of a penalty */ | ||||
| /* return BKE_gpencil_layer_find_frame(gpl, cfra); */ | /* return BKE_gpencil_layer_find_frame(gpl, cfra); */ | ||||
| Show All 22 Lines | int gp_active_layer_poll(bContext *C) | ||||
| return (gpl != NULL); | return (gpl != NULL); | ||||
| } | } | ||||
| /* poll callback for checking if there is an active brush */ | /* poll callback for checking if there is an active brush */ | ||||
| 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); | Paint *paint = &ts->gp_paint->paint; | ||||
| if (paint) { | |||||
| return (brush != NULL); | return (paint->brush != NULL); | ||||
| } | } | ||||
| else { | |||||
| /* poll callback for checking if there is an active palette */ | return false; | ||||
| 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( | ||||
| ▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf( | ||||
| RNA_enum_item_end(&item, &totitem); | RNA_enum_item_end(&item, &totitem); | ||||
| *r_free = true; | *r_free = true; | ||||
| return item; | return item; | ||||
| } | } | ||||
| /* ******************************************************** */ | /* ******************************************************** */ | ||||
| /* Brush Tool Core */ | /* Brush Tool Core */ | ||||
| /** | /** | ||||
| * Check whether a given stroke segment is inside a circular brush | * Check whether a given stroke segment is inside a circular brush | ||||
| * | * | ||||
| * \param mval The current screen-space coordinates (midpoint) of the brush | * \param mval The current screen-space coordinates (midpoint) of the brush | ||||
| * \param mvalo The previous screen-space coordinates (midpoint) of the brush (NOT CURRENTLY USED) | * \param mvalo The previous screen-space coordinates (midpoint) of the brush (NOT CURRENTLY USED) | ||||
| Show All 17 Lines | bool gp_stroke_inside_circle(const int mval[2], const int UNUSED(mvalo[2]), | ||||
| /* not inside */ | /* not inside */ | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* ******************************************************** */ | /* ******************************************************** */ | ||||
| /* Stroke Validity Testing */ | /* Stroke Validity Testing */ | ||||
| /* Check whether given stroke can be edited given the supplied context */ | /* Check whether given stroke can be edited given the supplied context */ | ||||
| // XXX: do we need additional flags for screenspace vs dataspace? | /* TODO: do we need additional flags for screenspace vs dataspace? */ | ||||
| bool ED_gpencil_stroke_can_use_direct(const ScrArea *sa, const bGPDstroke *gps) | bool ED_gpencil_stroke_can_use_direct(const ScrArea *sa, const bGPDstroke *gps) | ||||
| { | { | ||||
| /* 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); | ||||
| } | } | ||||
| else { | else { | ||||
| /* view aligned - anything goes */ | /* view aligned - anything goes */ | ||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| /* Check whether given stroke can be edited in the current context */ | /* Check whether given stroke can be edited in the current context */ | ||||
| bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) | 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(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps) | ||||
| { | { | ||||
| /* check if the color is editable */ | /* check if the color is editable */ | ||||
| bGPDpalettecolor *palcolor = gps->palcolor; | MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); | ||||
| if (palcolor != NULL) { | |||||
| if (palcolor->flag & PC_COLOR_HIDE) | if (gp_style != NULL) { | ||||
| if (gp_style->flag & GP_STYLE_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) && (gp_style->flag & GP_STYLE_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 44 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(Depsgraph *depsgraph, 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(depsgraph, 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(Depsgraph *depsgraph, 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(depsgraph, 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 148 Lines • ▼ Show 20 Lines | bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, Scene *scene, const float screen_co[2], float r_out[3]) | ||||
| else { | else { | ||||
| zero_v3(r_out); | zero_v3(r_out); | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| /** | /** | ||||
| * Apply smooth to stroke point | * Convert tGPspoint (temporary 2D/screenspace point data used by GP modal operators) | ||||
| * \param gps Stroke to smooth | * to 3D coordinates. | ||||
| * \param i Point index | * | ||||
| * \param inf Amount of smoothing to apply | * \param point2D: The screenspace 2D point data to convert | ||||
| * \param affect_pressure Apply smoothing to pressure values too? | * \param depth: Depth array (via ED_view3d_autodist_depth()) | ||||
| * \param[out] r_out: The resulting 2D point data | |||||
| */ | |||||
| void gp_stroke_convertcoords_tpoint( | |||||
| Scene *scene, ARegion *ar, View3D *v3d, | |||||
| Object *ob, bGPDlayer *gpl, | |||||
| const tGPspoint *point2D, float *depth, | |||||
| float r_out[3]) | |||||
| { | |||||
| ToolSettings *ts = scene->toolsettings; | |||||
| const int mval[2] = {point2D->x, point2D->y}; | |||||
| if ((depth != NULL) && (ED_view3d_autodist_simple(ar, mval, r_out, 0, depth))) { | |||||
| /* projecting onto 3D-Geometry | |||||
| * - nothing more needs to be done here, since view_autodist_simple() has already done it | |||||
| */ | */ | ||||
| bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure) | |||||
| { | |||||
| bGPDspoint *pt = &gps->points[i]; | |||||
| float pressure = 0.0f; | |||||
| float sco[3] = {0.0f}; | |||||
| /* Do nothing if not enough points to smooth out */ | |||||
| if (gps->totpoints <= 2) { | |||||
| return false; | |||||
| } | } | ||||
| else { | |||||
| float mval_f[2] = {(float)point2D->x, (float)point2D->y}; | |||||
| float mval_prj[2]; | |||||
| float rvec[3], dvec[3]; | |||||
| float zfac; | |||||
| /* Only affect endpoints by a fraction of the normal strength, | /* Current method just converts each point in screen-coordinates to | ||||
| * to prevent the stroke from shrinking too much | * 3D-coordinates using the 3D-cursor as reference. | ||||
| */ | */ | ||||
| if ((i == 0) || (i == gps->totpoints - 1)) { | ED_gp_get_drawing_reference(v3d, scene, ob, gpl, ts->gpencil_v3d_align, rvec); | ||||
| inf *= 0.1f; | zfac = ED_view3d_calc_zfac(ar->regiondata, rvec, NULL); | ||||
| if (ED_view3d_project_float_global(ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { | |||||
| sub_v2_v2v2(mval_f, mval_prj, mval_f); | |||||
| ED_view3d_win_to_delta(ar, mval_f, dvec, zfac); | |||||
| sub_v3_v3v3(r_out, rvec, dvec); | |||||
| } | |||||
| else { | |||||
| zero_v3(r_out); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| /* Compute smoothed coordinate by taking the ones nearby */ | /** | ||||
| /* XXX: This is potentially slow, and suffers from accumulation error as earlier points are handled before later ones */ | * Get drawing reference point for conversion or projection of the stroke | ||||
| * \param[out] r_vec : Reference point found | |||||
| */ | |||||
| void ED_gp_get_drawing_reference(View3D *v3d, Scene *scene, Object *ob, bGPDlayer *gpl, char align_flag, float r_vec[3]) | |||||
| { | { | ||||
| // XXX: this is hardcoded to look at 2 points on either side of the current one (i.e. 5 items total) | const float *fp = ED_view3d_cursor3d_get(scene, v3d)->location; | ||||
| const int steps = 2; | |||||
| const float average_fac = 1.0f / (float)(steps * 2 + 1); | |||||
| int step; | |||||
| /* add the point itself */ | |||||
| madd_v3_v3fl(sco, &pt->x, average_fac); | |||||
| if (affect_pressure) { | /* if using a gpencil object at cursor mode, can use the location of the object */ | ||||
| pressure += pt->pressure * average_fac; | if (align_flag & GP_PROJECT_VIEWSPACE) { | ||||
| if (ob && (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(r_vec, &gps->points[gps->totpoints - 1].x); | |||||
| mul_m4_v3(ob->obmat, r_vec); | |||||
| return; | |||||
| } | } | ||||
| /* n-steps before/after current point */ | |||||
| // XXX: review how the endpoints are treated by this algorithm | |||||
| // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight | |||||
| for (step = 1; step <= steps; step++) { | |||||
| bGPDspoint *pt1, *pt2; | |||||
| int before = i - step; | |||||
| int after = i + step; | |||||
| CLAMP_MIN(before, 0); | |||||
| CLAMP_MAX(after, gps->totpoints - 1); | |||||
| pt1 = &gps->points[before]; | |||||
| pt2 = &gps->points[after]; | |||||
| /* add both these points to the average-sum (s += p[i]/n) */ | |||||
| madd_v3_v3fl(sco, &pt1->x, average_fac); | |||||
| madd_v3_v3fl(sco, &pt2->x, average_fac); | |||||
| #if 0 | |||||
| /* XXX: Disabled because get weird result */ | |||||
| /* do pressure too? */ | |||||
| if (affect_pressure) { | |||||
| pressure += pt1->pressure * average_fac; | |||||
| pressure += pt2->pressure * average_fac; | |||||
| } | } | ||||
| #endif | |||||
| } | } | ||||
| } | } | ||||
| /* fallback (no strokes) - use cursor or object location */ | |||||
| /* Based on influence factor, blend between original and optimal smoothed coordinate */ | if (align_flag & GP_PROJECT_CURSOR) { | ||||
| interp_v3_v3v3(&pt->x, &pt->x, sco, inf); | /* use 3D-cursor */ | ||||
| copy_v3_v3(r_vec, fp); | |||||
| #if 0 | |||||
| /* XXX: Disabled because get weird result */ | |||||
| if (affect_pressure) { | |||||
| pt->pressure = pressure; | |||||
| } | } | ||||
| #endif | else { | ||||
| /* use object location */ | |||||
| return true; | copy_v3_v3(r_vec, ob->obmat[3]); | ||||
| } | } | ||||
| } | |||||
| } | |||||
| else { | |||||
| /* use 3D-cursor */ | |||||
| copy_v3_v3(r_vec, fp); | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * Apply smooth for strength to stroke point | * Reproject all points of the stroke to a plane locked to axis to avoid stroke offset | ||||
| * \param gps Stroke to smooth | |||||
| * \param i Point index | |||||
| * \param inf Amount of smoothing to apply | |||||
| */ | */ | ||||
| bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf) | void ED_gp_project_stroke_to_plane(Object *ob, RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis, char UNUSED(type)) | ||||
| { | { | ||||
| bGPDspoint *ptb = &gps->points[i]; | float plane_normal[3]; | ||||
| float vn[3]; | |||||
| /* Do nothing if not enough points */ | float ray[3]; | ||||
| if (gps->totpoints <= 2) { | float rpoint[3]; | ||||
| return false; | |||||
| } | |||||
| /* Compute theoretical optimal value using distances */ | |||||
| bGPDspoint *pta, *ptc; | |||||
| int before = i - 1; | |||||
| int after = i + 1; | |||||
| CLAMP_MIN(before, 0); | /* normal vector for a plane locked to axis */ | ||||
| CLAMP_MAX(after, gps->totpoints - 1); | zero_v3(plane_normal); | ||||
| if (axis < 0) { | |||||
| pta = &gps->points[before]; | /* if the axis is not locked, need a vector to the view direction | ||||
| ptc = &gps->points[after]; | * in order to get the right size of the stroke. | ||||
| /* the optimal value is the corresponding to the interpolation of the strength | |||||
| * at the distance of point b | |||||
| */ | */ | ||||
| const float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); | ED_view3d_global_to_vector(rv3d, origin, plane_normal); | ||||
| const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength; | } | ||||
| else { | |||||
| plane_normal[axis] = 1.0f; | |||||
| /* if object, apply object rotation */ | |||||
| if (ob && (ob->type == OB_GPENCIL)) { | |||||
| mul_mat3_m4_v3(ob->obmat, plane_normal); | |||||
| } | |||||
| } | |||||
| /* Based on influence factor, blend between original and optimal */ | /* Reproject the points in the plane */ | ||||
| ptb->strength = (1.0f - inf) * ptb->strength + inf * optimal; | for (int i = 0; i < gps->totpoints; i++) { | ||||
| bGPDspoint *pt = &gps->points[i]; | |||||
| return true; | /* 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 extreme 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); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| /** | /** | ||||
| * Apply smooth for thickness to stroke point (use pressure) | * Reproject given point to a plane locked to axis to avoid stroke offset | ||||
| * \param gps Stroke to smooth | * \param[in, out] pt : Point to affect | ||||
| * \param i Point index | |||||
| * \param inf Amount of smoothing to apply | |||||
| */ | */ | ||||
| bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf) | void ED_gp_project_point_to_plane(Object *ob, RegionView3D *rv3d, const float origin[3], const int axis, char UNUSED(type), bGPDspoint *pt) | ||||
| { | { | ||||
| bGPDspoint *ptb = &gps->points[i]; | float plane_normal[3]; | ||||
| float vn[3]; | |||||
| /* Do nothing if not enough points */ | |||||
| if (gps->totpoints <= 2) { | |||||
| return false; | |||||
| } | |||||
| /* Compute theoretical optimal value using distances */ | float ray[3]; | ||||
| bGPDspoint *pta, *ptc; | float rpoint[3]; | ||||
| int before = i - 1; | |||||
| int after = i + 1; | |||||
| CLAMP_MIN(before, 0); | /* normal vector for a plane locked to axis */ | ||||
| CLAMP_MAX(after, gps->totpoints - 1); | zero_v3(plane_normal); | ||||
| if (axis < 0) { | |||||
| /* if the axis is not locked, need a vector to the view direction | |||||
| * in order to get the right size of the stroke. | |||||
| */ | |||||
| ED_view3d_global_to_vector(rv3d, origin, plane_normal); | |||||
| } | |||||
| else { | |||||
| plane_normal[axis] = 1.0f; | |||||
| /* if object, apply object rotation */ | |||||
| if (ob && (ob->type == OB_GPENCIL)) { | |||||
| mul_mat3_m4_v3(ob->obmat, plane_normal); | |||||
| } | |||||
| } | |||||
| pta = &gps->points[before]; | |||||
| ptc = &gps->points[after]; | |||||
| /* the optimal value is the corresponding to the interpolation of the pressure | /* Reproject the points in the plane */ | ||||
| * at the distance of point b | /* get a vector from the point with the current view direction of the viewport */ | ||||
| */ | ED_view3d_global_to_vector(rv3d, &pt->x, vn); | ||||
| float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); | |||||
| float optimal = (1.0f - fac) * pta->pressure + fac * ptc->pressure; | |||||
| /* Based on influence factor, blend between original and optimal */ | /* calculate line extrem point to create a ray that cross the plane */ | ||||
| ptb->pressure = (1.0f - inf) * ptb->pressure + inf * optimal; | mul_v3_fl(vn, -50.0f); | ||||
| add_v3_v3v3(ray, &pt->x, vn); | |||||
| return true; | /* 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); | |||||
| } | } | ||||
| } | |||||
| /* ******************************************************** */ | |||||
| /* Stroke Operations */ | |||||
| // XXX: Check if these functions duplicate stuff in blenkernel, and/or whether we should just deduplicate | |||||
| /** | /** | ||||
| * Subdivide a stroke once, by adding a point half way between each pair of existing points | * Subdivide a stroke once, by adding a point half way between each pair of existing points | ||||
| * \param gps Stroke data | * \param gps Stroke data | ||||
| * \param new_totpoints Total number of points (after subdividing) | * \param subdivide Number of times to subdivide | ||||
| */ | */ | ||||
| void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) | void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide) | ||||
| { | { | ||||
| /* Move points towards end of enlarged points array to leave space for new points */ | bGPDspoint *temp_points; | ||||
| int y = 1; | int totnewpoints, oldtotpoints; | ||||
| for (int i = gps->totpoints - 1; i > 0; i--) { | int i2; | ||||
| gps->points[new_totpoints - y] = gps->points[i]; | |||||
| y += 2; | /* loop as many times as levels */ | ||||
| } | for (int s = 0; s < subdivide; s++) { | ||||
| totnewpoints = gps->totpoints - 1; | |||||
| /* Create interpolated points */ | /* duplicate points in a temp area */ | ||||
| for (int i = 0; i < new_totpoints - 1; i += 2) { | temp_points = MEM_dupallocN(gps->points); | ||||
| bGPDspoint *prev = &gps->points[i]; | oldtotpoints = gps->totpoints; | ||||
| bGPDspoint *pt = &gps->points[i + 1]; | |||||
| bGPDspoint *next = &gps->points[i + 2]; | /* resize the points arrys */ | ||||
| gps->totpoints += totnewpoints; | |||||
| /* Interpolate all values */ | gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); | ||||
| interp_v3_v3v3(&pt->x, &prev->x, &next->x, 0.5f); | gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); | ||||
| gps->flag |= GP_STROKE_RECALC_CACHES; | |||||
| /* move points from last to first to new place */ | |||||
| i2 = gps->totpoints - 1; | |||||
| for (int i = oldtotpoints - 1; i > 0; i--) { | |||||
| bGPDspoint *pt = &temp_points[i]; | |||||
| bGPDspoint *pt_final = &gps->points[i2]; | |||||
| MDeformVert *dvert = &gps->dvert[i]; | |||||
| MDeformVert *dvert_final = &gps->dvert[i2]; | |||||
| copy_v3_v3(&pt_final->x, &pt->x); | |||||
| pt_final->pressure = pt->pressure; | |||||
| pt_final->strength = pt->strength; | |||||
| pt_final->time = pt->time; | |||||
| pt_final->flag = pt->flag; | |||||
| pt_final->uv_fac = pt->uv_fac; | |||||
| pt_final->uv_rot = pt->uv_rot; | |||||
| dvert_final->totweight = dvert->totweight; | |||||
| dvert_final->dw = dvert->dw; | |||||
| i2 -= 2; | |||||
| } | |||||
| /* interpolate mid points */ | |||||
| i2 = 1; | |||||
| for (int i = 0; i < oldtotpoints - 1; i++) { | |||||
| bGPDspoint *pt = &temp_points[i]; | |||||
| bGPDspoint *next = &temp_points[i + 1]; | |||||
| bGPDspoint *pt_final = &gps->points[i2]; | |||||
| MDeformVert *dvert_final = &gps->dvert[i2]; | |||||
| /* add a half way point */ | |||||
| interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); | |||||
| pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); | |||||
| pt_final->strength = interpf(pt->strength, next->strength, 0.5f); | |||||
| CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); | |||||
| pt_final->time = interpf(pt->time, next->time, 0.5f); | |||||
| pt_final->uv_fac = interpf(pt->uv_fac, next->uv_fac, 0.5f); | |||||
| pt_final->uv_rot = interpf(pt->uv_rot, next->uv_rot, 0.5f); | |||||
| dvert_final->totweight = 0; | |||||
| dvert_final->dw = NULL; | |||||
| i2 += 2; | |||||
| } | |||||
| MEM_SAFE_FREE(temp_points); | |||||
| /* move points to smooth stroke */ | |||||
| /* duplicate points in a temp area with the new subdivide data */ | |||||
| temp_points = MEM_dupallocN(gps->points); | |||||
| /* extreme points are not changed */ | |||||
| for (int i = 0; i < gps->totpoints - 2; i++) { | |||||
| bGPDspoint *pt = &temp_points[i]; | |||||
| bGPDspoint *next = &temp_points[i + 1]; | |||||
| bGPDspoint *pt_final = &gps->points[i + 1]; | |||||
| pt->pressure = interpf(prev->pressure, next->pressure, 0.5f); | /* move point */ | ||||
| pt->strength = interpf(prev->strength, next->strength, 0.5f); | interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); | ||||
| CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); | } | ||||
| pt->time = interpf(prev->time, next->time, 0.5f); | /* free temp memory */ | ||||
| MEM_SAFE_FREE(temp_points); | |||||
| } | } | ||||
| /* Update to new total number of points */ | |||||
| gps->totpoints = new_totpoints; | |||||
| } | } | ||||
| /** | /** | ||||
| * Add randomness to stroke | * Add randomness to stroke | ||||
| * \param gps Stroke data | * \param gps Stroke data | ||||
| * \param brush Brush data | * \param brush Brush data | ||||
| */ | */ | ||||
| void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush) | void gp_randomize_stroke(bGPDstroke *gps, Brush *brush) | ||||
| { | { | ||||
| bGPDspoint *pt1, *pt2, *pt3; | bGPDspoint *pt1, *pt2, *pt3; | ||||
| float v1[3]; | float v1[3]; | ||||
| float v2[3]; | float v2[3]; | ||||
| if (gps->totpoints < 3) { | if (gps->totpoints < 3) { | ||||
| return; | return; | ||||
| } | } | ||||
| Show All 13 Lines | void gp_randomize_stroke(bGPDstroke *gps, Brush *brush) | ||||
| normalize_v3(normal); | normalize_v3(normal); | ||||
| /* get orthogonal vector to plane to rotate random effect */ | /* get orthogonal vector to plane to rotate random effect */ | ||||
| float ortho[3]; | float ortho[3]; | ||||
| cross_v3_v3v3(ortho, v1, normal); | cross_v3_v3v3(ortho, v1, normal); | ||||
| normalize_v3(ortho); | normalize_v3(ortho); | ||||
| /* Read all points and apply shift vector (first and last point not modified) */ | /* Read all points and apply shift vector (first and last point not modified) */ | ||||
| for (int i = 1; i < gps->totpoints - 1; ++i) { | for (int i = 1; i < gps->totpoints - 1; i++) { | ||||
| bGPDspoint *pt = &gps->points[i]; | bGPDspoint *pt = &gps->points[i]; | ||||
| /* get vector with shift (apply a division because random is too sensitive */ | /* get vector with shift (apply a division because random is too sensitive */ | ||||
| const float fac = BLI_frand() * (brush->draw_random_sub / 10.0f); | const float fac = BLI_frand() * (brush->gpencil_settings->draw_random_sub / 10.0f); | ||||
| float svec[3]; | float svec[3]; | ||||
| copy_v3_v3(svec, ortho); | copy_v3_v3(svec, ortho); | ||||
| if (BLI_frand() > 0.5f) { | if (BLI_frand() > 0.5f) { | ||||
| mul_v3_fl(svec, -fac); | mul_v3_fl(svec, -fac); | ||||
| } | } | ||||
| else { | else { | ||||
| mul_v3_fl(svec, fac); | mul_v3_fl(svec, fac); | ||||
| } | } | ||||
| /* apply shift */ | /* apply shift */ | ||||
| add_v3_v3(&pt->x, svec); | add_v3_v3(&pt->x, svec); | ||||
| } | } | ||||
| } | } | ||||
| /* calculate difference matrix */ | |||||
| void ED_gpencil_parent_location(bGPDlayer *gpl, float diff_mat[4][4]) | |||||
| { | |||||
| Object *ob = gpl->parent; | |||||
| if (ob == NULL) { | /* ******************************************************** */ | ||||
| /* Layer Parenting - Compute Parent Transforms */ | |||||
| /* calculate difference matrix */ | |||||
| void ED_gpencil_parent_location( | |||||
| const Depsgraph *depsgraph, Object *obact, bGPdata *UNUSED(gpd), | |||||
| bGPDlayer *gpl, float diff_mat[4][4]) | |||||
| { | |||||
| Object *ob_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obact) : obact; | |||||
| Object *obparent = gpl->parent; | |||||
| Object *obparent_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obparent) : obparent; | |||||
| /* if not layer parented, try with object parented */ | |||||
| if (obparent_eval == NULL) { | |||||
| if (ob_eval != NULL) { | |||||
| if (ob_eval->type == OB_GPENCIL) { | |||||
| copy_m4_m4(diff_mat, ob_eval->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_eval->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_eval->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_eval->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_eval->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(Depsgraph *depsgraph, 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(depsgraph, 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 */ | ||||
| copy_m4_m4(gpl->inverse, cur_mat); | copy_m4_m4(gpl->inverse, cur_mat); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* ******************************************************** */ | /* ******************************************************** */ | ||||
| bool ED_gpencil_stroke_minmax( | /* GP Object Stuff */ | ||||
| const bGPDstroke *gps, const bool use_select, | |||||
| float r_min[3], float r_max[3]) | /* Helper function to create new OB_GPENCIL Object */ | ||||
| Object *ED_add_gpencil_object(bContext *C, Scene *scene, const float loc[3]) | |||||
| { | { | ||||
| const bGPDspoint *pt; | float rot[3] = {0.0f}; | ||||
| int i; | |||||
| bool changed = false; | |||||
| for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | Object *ob = ED_object_add_type(C, OB_GPENCIL, NULL, loc, rot, false, scene->lay); | ||||
| if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) {; | |||||
| minmax_v3v3_v3(r_min, r_max, &pt->x); | /* define size */ | ||||
| changed = true; | 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) | |||||
| { | |||||
| Main *bmain = CTX_data_main(C); | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| /* first try to reuse default material */ | |||||
| if (ob->actcol > 0) { | |||||
| Material *ma = give_current_material(ob, ob->actcol); | |||||
| if ((ma) && (ma->gp_style == NULL)) { | |||||
| BKE_material_init_gpencil_settings(ma); | |||||
| } | } | ||||
| } | } | ||||
| return changed; | |||||
| /* ensure color exist */ | |||||
| BKE_gpencil_material_ensure(bmain, ob); | |||||
| Paint *paint = BKE_brush_get_gpencil_paint(ts); | |||||
| /* if not exist, create a new one */ | |||||
| if (paint->brush == NULL) { | |||||
| /* create new brushes */ | |||||
| BKE_brush_gpencil_presets(C); | |||||
| } | } | ||||
| /* Dynamic Enums of GP Brushes */ | } | ||||
| const EnumPropertyItem *ED_gpencil_brushes_enum_itemf( | |||||
| bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), | /* ******************************************************** */ | ||||
| bool *r_free) | /* Vertex Groups */ | ||||
| /* assign points to vertex group */ | |||||
| void ED_gpencil_vgroup_assign(bContext *C, Object *ob, float weight) | |||||
| { | { | ||||
| ToolSettings *ts = CTX_data_tool_settings(C); | const int def_nr = ob->actdef - 1; | ||||
| bGPDbrush *brush; | if (!BLI_findlink(&ob->defbase, def_nr)) | ||||
| EnumPropertyItem *item = NULL, item_tmp = { 0 }; | return; | ||||
| int totitem = 0; | |||||
| int i = 0; | |||||
| if (ELEM(NULL, C, ts)) { | CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | ||||
| return DummyRNA_DEFAULT_items; | { | ||||
| if (gps->flag & GP_STROKE_SELECT) { | |||||
| for (int i = 0; i < gps->totpoints; i++) { | |||||
| bGPDspoint *pt = &gps->points[i]; | |||||
| MDeformVert *dvert = &gps->dvert[i]; | |||||
| if (pt->flag & GP_SPOINT_SELECT) { | |||||
| BKE_gpencil_vgroup_add_point_weight(dvert, def_nr, weight); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| } | } | ||||
| /* Existing brushes */ | /* remove points from vertex group */ | ||||
| for (brush = ts->gp_brushes.first; brush; brush = brush->next, i++) { | void ED_gpencil_vgroup_remove(bContext *C, Object *ob) | ||||
| item_tmp.identifier = brush->info; | { | ||||
| item_tmp.name = brush->info; | const int def_nr = ob->actdef - 1; | ||||
| item_tmp.value = i; | if (!BLI_findlink(&ob->defbase, def_nr)) | ||||
| return; | |||||
| if (brush->flag & GP_BRUSH_ACTIVE) | CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | ||||
| item_tmp.icon = ICON_BRUSH_DATA; | { | ||||
| else | for (int i = 0; i < gps->totpoints; i++) { | ||||
| item_tmp.icon = ICON_NONE; | bGPDspoint *pt = &gps->points[i]; | ||||
| MDeformVert *dvert = &gps->dvert[i]; | |||||
| RNA_enum_item_add(&item, &totitem, &item_tmp); | if ((pt->flag & GP_SPOINT_SELECT) && (dvert->totweight > 0)) { | ||||
| BKE_gpencil_vgroup_remove_point_weight(dvert, def_nr); | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| } | } | ||||
| RNA_enum_item_end(&item, &totitem); | /* select points of vertex group */ | ||||
| *r_free = true; | void ED_gpencil_vgroup_select(bContext *C, Object *ob) | ||||
| { | |||||
| const int def_nr = ob->actdef - 1; | |||||
| if (!BLI_findlink(&ob->defbase, def_nr)) | |||||
| return; | |||||
| return item; | CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | ||||
| { | |||||
| for (int i = 0; i < gps->totpoints; i++) { | |||||
| bGPDspoint *pt = &gps->points[i]; | |||||
| MDeformVert *dvert = &gps->dvert[i]; | |||||
| if (BKE_gpencil_vgroup_use_index(dvert, def_nr) > -1.0f) { | |||||
| pt->flag |= GP_SPOINT_SELECT; | |||||
| gps->flag |= GP_STROKE_SELECT; | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| } | } | ||||
| /* Dynamic Enums of GP Palettes */ | /* unselect points of vertex group */ | ||||
| const EnumPropertyItem *ED_gpencil_palettes_enum_itemf( | void ED_gpencil_vgroup_deselect(bContext *C, Object *ob) | ||||
| bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), | |||||
| bool *r_free) | |||||
| { | { | ||||
| bGPdata *gpd = CTX_data_gpencil_data(C); | const int def_nr = ob->actdef - 1; | ||||
| bGPDpalette *palette; | if (!BLI_findlink(&ob->defbase, def_nr)) | ||||
| EnumPropertyItem *item = NULL, item_tmp = { 0 }; | return; | ||||
| int totitem = 0; | |||||
| int i = 0; | |||||
| if (ELEM(NULL, C, gpd)) { | CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) | ||||
| return DummyRNA_DEFAULT_items; | { | ||||
| for (int i = 0; i < gps->totpoints; i++) { | |||||
| bGPDspoint *pt = &gps->points[i]; | |||||
| MDeformVert *dvert = &gps->dvert[i]; | |||||
| if (BKE_gpencil_vgroup_use_index(dvert, def_nr) > -1.0f) { | |||||
| pt->flag &= ~GP_SPOINT_SELECT; | |||||
| gps->flag |= GP_STROKE_SELECT; | |||||
| } | |||||
| } | |||||
| } | |||||
| CTX_DATA_END; | |||||
| } | } | ||||
| /* Existing palettes */ | /* ******************************************************** */ | ||||
| for (palette = gpd->palettes.first; palette; palette = palette->next, i++) { | /* Cursor drawing */ | ||||
| item_tmp.identifier = palette->info; | |||||
| item_tmp.name = palette->info; | |||||
| item_tmp.value = i; | |||||
| if (palette->flag & PL_PALETTE_ACTIVE) | /* check if cursor is in drawing region */ | ||||
| item_tmp.icon = ICON_COLOR; | static bool gp_check_cursor_region(bContext *C, int mval[2]) | ||||
| else | { | ||||
| item_tmp.icon = ICON_NONE; | 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; | |||||
| RNA_enum_item_add(&item, &totitem, &item_tmp); | /* Perform bounds check using */ | ||||
| ED_region_visible_rect(ar, ®ion_rect); | |||||
| return BLI_rcti_isect_pt_v(®ion_rect, mval); | |||||
| } | |||||
| else { | |||||
| return false; | |||||
| } | |||||
| } | } | ||||
| RNA_enum_item_end(&item, &totitem); | /* draw eraser cursor */ | ||||
| *r_free = true; | void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y) | ||||
| { | |||||
| short radius = (short)brush->size; | |||||
| return item; | Gwn_VertFormat *format = immVertexFormat(); | ||||
| const uint shdr_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); | |||||
| glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |||||
| immUniformColor4ub(255, 100, 100, 20); | |||||
| imm_draw_circle_fill_2d(shdr_pos, x, y, radius, 40); | |||||
| immUnbindProgram(); | |||||
| immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); | |||||
| float viewport_size[4]; | |||||
| glGetFloatv(GL_VIEWPORT, viewport_size); | |||||
| immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); | |||||
| immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); | |||||
| immUniform1i("num_colors", 0); /* "simple" mode */ | |||||
| immUniform1f("dash_width", 12.0f); | |||||
| immUniform1f("dash_factor", 0.5f); | |||||
| imm_draw_circle_wire_2d(shdr_pos, x, y, radius, | |||||
| /* XXX Dashed shader gives bad results with sets of small segments currently, | |||||
| * temp hack around the issue. :( */ | |||||
| max_ii(8, radius / 2)); /* was fixed 40 */ | |||||
| immUnbindProgram(); | |||||
| glDisable(GL_BLEND); | |||||
| glDisable(GL_LINE_SMOOTH); | |||||
| } | |||||
| /* Helper callback for drawing the cursor itself */ | |||||
| static void gp_brush_drawcursor(bContext *C, int x, int y, void *customdata) | |||||
| { | |||||
| Main *bmain = CTX_data_main(C); | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| Object *ob = CTX_data_active_object(C); | |||||
| ARegion *ar = CTX_wm_region(C); | |||||
| GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; | |||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | |||||
| GP_EditBrush_Data *brush = NULL; | |||||
| Brush *paintbrush = NULL; | |||||
| Material *ma = NULL; | |||||
| MaterialGPencilStyle *gp_style = NULL; | |||||
| int *last_mouse_position = customdata; | |||||
| if ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) { | |||||
| brush = &gset->brush[gset->weighttype]; | |||||
| } | |||||
| else { | |||||
| brush = &gset->brush[gset->brushtype]; | |||||
| } | |||||
| /* default radius and color */ | |||||
| float color[3] = {1.0f, 1.0f, 1.0f}; | |||||
| float darkcolor[3]; | |||||
| float radius = 3.0f; | |||||
| int mval[2] = {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) { | |||||
| paintbrush = BKE_brush_getactive_gpencil(scene->toolsettings); | |||||
| /* while drawing hide */ | |||||
| if ((gpd->sbuffer_size > 0) && | |||||
| (paintbrush) && ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && | |||||
| ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0)) | |||||
| { | |||||
| return; | |||||
| } | |||||
| if (paintbrush) { | |||||
| if ((paintbrush->gpencil_settings->flag & GP_BRUSH_ENABLE_CURSOR) == 0) { | |||||
| return; | |||||
| } | |||||
| /* eraser has special shape and use a different shader program */ | |||||
| if (paintbrush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE) { | |||||
| ED_gpencil_brush_draw_eraser(paintbrush, x, y); | |||||
| return; | |||||
| } | |||||
| /* get current drawing color */ | |||||
| ma = BKE_gpencil_get_material_from_brush(paintbrush); | |||||
| if (ma == NULL) { | |||||
| BKE_gpencil_material_ensure(bmain, ob); | |||||
| /* assign the first material to the brush */ | |||||
| ma = give_current_material(ob, 1); | |||||
| paintbrush->gpencil_settings->material = ma; | |||||
| } | |||||
| gp_style = ma->gp_style; | |||||
| /* 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. | |||||
| */ | |||||
| if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && | |||||
| ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && | |||||
| ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && | |||||
| (paintbrush->gpencil_settings->brush_type == GP_BRUSH_TYPE_DRAW)) | |||||
| { | |||||
| radius = 2.0f; | |||||
| copy_v3_v3(color, gp_style->stroke_rgba); | |||||
| } | |||||
| else { | |||||
| radius = 5.0f; | |||||
| copy_v3_v3(color, paintbrush->add_col); | |||||
| } | |||||
| } | |||||
| else { | |||||
| return; | |||||
| } | |||||
| } | |||||
| /* 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); | |||||
| if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && | |||||
| ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && | |||||
| ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && | |||||
| (paintbrush->gpencil_settings->brush_type == GP_BRUSH_TYPE_DRAW)) | |||||
| { | |||||
| imm_draw_circle_fill_2d(pos, x, y, radius, 40); | |||||
| } | |||||
| else { | |||||
| 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); | |||||
| glDisable(GL_BLEND); | |||||
| glDisable(GL_LINE_SMOOTH); | |||||
| /* Draw line for lazy mouse */ | |||||
| if (last_mouse_position) { | |||||
| glEnable(GL_LINE_SMOOTH); | |||||
| glEnable(GL_BLEND); | |||||
| copy_v3_v3(color, paintbrush->add_col); | |||||
| immUniformColor4f(color[0], color[1], color[2], 0.8f); | |||||
| immBegin(GWN_PRIM_LINES, 2); | |||||
| immVertex2f(pos, x, y); | |||||
| immVertex2f(pos, last_mouse_position[0] + ar->winrct.xmin, | |||||
| last_mouse_position[1] + ar->winrct.ymin); | |||||
| immEnd(); | |||||
| glDisable(GL_BLEND); | |||||
| glDisable(GL_LINE_SMOOTH); | |||||
| } | |||||
| immUnbindProgram(); | |||||
| } | |||||
| /* Turn brush cursor in on/off */ | |||||
| void ED_gpencil_toggle_brush_cursor(bContext *C, bool enable, void *customdata) | |||||
| { | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; | |||||
| int *lastpost = customdata; | |||||
| 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, | |||||
| (lastpost) ? customdata : NULL); | |||||
| } | |||||
| } | |||||
| /* verify if is using the right brush */ | |||||
| static void gpencil_verify_brush_type(bContext *C, int newmode) | |||||
| { | |||||
| ToolSettings *ts = CTX_data_tool_settings(C); | |||||
| GP_BrushEdit_Settings *gset = &ts->gp_sculpt; | |||||
| switch (newmode) { | |||||
| case OB_MODE_GPENCIL_SCULPT: | |||||
| gset->flag &= ~GP_BRUSHEDIT_FLAG_WEIGHT_MODE; | |||||
| if ((gset->brushtype < 0) || (gset->brushtype >= GP_EDITBRUSH_TYPE_WEIGHT)) { | |||||
| gset->brushtype = GP_EDITBRUSH_TYPE_PUSH; | |||||
| } | |||||
| break; | |||||
| case OB_MODE_GPENCIL_WEIGHT: | |||||
| gset->flag |= GP_BRUSHEDIT_FLAG_WEIGHT_MODE; | |||||
| if ((gset->weighttype < GP_EDITBRUSH_TYPE_WEIGHT) || (gset->weighttype >= TOT_GP_EDITBRUSH_TYPES)) { | |||||
| gset->weighttype = GP_EDITBRUSH_TYPE_WEIGHT; | |||||
| } | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* set object modes */ | |||||
| void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) | |||||
| { | |||||
| if (!gpd) { | |||||
| return; | |||||
| } | |||||
| switch (newmode) { | |||||
| case OB_MODE_GPENCIL_EDIT: | |||||
| gpd->flag |= GP_DATA_STROKE_EDITMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; | |||||
| ED_gpencil_toggle_brush_cursor(C, false, NULL); | |||||
| break; | |||||
| case OB_MODE_GPENCIL_PAINT: | |||||
| gpd->flag &= ~GP_DATA_STROKE_EDITMODE; | |||||
| gpd->flag |= GP_DATA_STROKE_PAINTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; | |||||
| ED_gpencil_toggle_brush_cursor(C, true, NULL); | |||||
| break; | |||||
| case OB_MODE_GPENCIL_SCULPT: | |||||
| gpd->flag &= ~GP_DATA_STROKE_EDITMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; | |||||
| gpd->flag |= GP_DATA_STROKE_SCULPTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; | |||||
| gpencil_verify_brush_type(C, OB_MODE_GPENCIL_SCULPT); | |||||
| ED_gpencil_toggle_brush_cursor(C, true, NULL); | |||||
| break; | |||||
| case OB_MODE_GPENCIL_WEIGHT: | |||||
| gpd->flag &= ~GP_DATA_STROKE_EDITMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; | |||||
| gpd->flag |= GP_DATA_STROKE_WEIGHTMODE; | |||||
| gpencil_verify_brush_type(C, OB_MODE_GPENCIL_WEIGHT); | |||||
| ED_gpencil_toggle_brush_cursor(C, true, NULL); | |||||
| break; | |||||
| default: | |||||
| gpd->flag &= ~GP_DATA_STROKE_EDITMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; | |||||
| gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; | |||||
| ED_gpencil_toggle_brush_cursor(C, false, NULL); | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* helper to convert 2d to 3d for simple drawing buffer */ | |||||
| static void gpencil_stroke_convertcoords(ARegion *ar, const tGPspoint *point2D, float origin[3], float out[3]) | |||||
| { | |||||
| float mval_f[2] = { (float)point2D->x, (float)point2D->y }; | |||||
| float mval_prj[2]; | |||||
| float rvec[3], dvec[3]; | |||||
| float zfac; | |||||
| copy_v3_v3(rvec, origin); | |||||
| zfac = ED_view3d_calc_zfac(ar->regiondata, rvec, NULL); | |||||
| if (ED_view3d_project_float_global(ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { | |||||
| sub_v2_v2v2(mval_f, mval_prj, mval_f); | |||||
| ED_view3d_win_to_delta(ar, mval_f, dvec, zfac); | |||||
| sub_v3_v3v3(out, rvec, dvec); | |||||
| } | |||||
| else { | |||||
| zero_v3(out); | |||||
| } | |||||
| } | |||||
| /* convert 2d tGPspoint to 3d bGPDspoint */ | |||||
| void ED_gpencil_tpoint_to_point(ARegion *ar, float origin[3], const tGPspoint *tpt, bGPDspoint *pt) | |||||
| { | |||||
| float p3d[3]; | |||||
| /* conversion to 3d format */ | |||||
| gpencil_stroke_convertcoords(ar, tpt, origin, p3d); | |||||
| copy_v3_v3(&pt->x, p3d); | |||||
| pt->pressure = tpt->pressure; | |||||
| pt->strength = tpt->strength; | |||||
| pt->uv_fac = tpt->uv_fac; | |||||
| pt->uv_rot = tpt->uv_rot; | |||||
| } | |||||
| /* texture coordinate utilities */ | |||||
| void ED_gpencil_calc_stroke_uv(Object *ob, bGPDstroke *gps) | |||||
| { | |||||
| if (gps == NULL) { | |||||
| return; | |||||
| } | |||||
| MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); | |||||
| float pixsize; | |||||
| if (gp_style) { | |||||
| pixsize = gp_style->texture_pixsize / 1000000.0f; | |||||
| } | |||||
| else { | |||||
| /* use this value by default */ | |||||
| pixsize = 0.000100f; | |||||
| } | |||||
| pixsize = MAX2(pixsize, 0.0000001f); | |||||
| bGPDspoint *pt = NULL; | |||||
| bGPDspoint *ptb = NULL; | |||||
| int i; | |||||
| float totlen = 0; | |||||
| /* first read all points and calc distance */ | |||||
| for (i = 0; i < gps->totpoints; i++) { | |||||
| pt = &gps->points[i]; | |||||
| /* first point */ | |||||
| if (i == 0) { | |||||
| pt->uv_fac = 0.0f; | |||||
| continue; | |||||
| } | |||||
| ptb = &gps->points[i - 1]; | |||||
| totlen += len_v3v3(&pt->x, &ptb->x) / pixsize; | |||||
| pt->uv_fac = totlen; | |||||
| } | |||||
| /* normalize the distance using a factor */ | |||||
| float factor; | |||||
| /* if image, use texture width */ | |||||
| if ((gp_style) && (gp_style->sima)) { | |||||
| factor = gp_style->sima->gen_x; | |||||
| } | |||||
| else { | |||||
| factor = totlen; | |||||
| } | |||||
| for (i = 0; i < gps->totpoints; i++) { | |||||
| pt = &gps->points[i]; | |||||
| pt->uv_fac /= factor; | |||||
| } | |||||
| } | |||||
| /* recalc uv for any stroke using the material */ | |||||
| void ED_gpencil_update_color_uv(Main *bmain, Material *mat) | |||||
| { | |||||
| Material *gps_ma = NULL; | |||||
| /* read all strokes */ | |||||
| for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { | |||||
| if (ob->type == OB_GPENCIL) { | |||||
| bGPdata *gpd = ob->data; | |||||
| if (gpd == NULL) { | |||||
| continue; | |||||
| } | |||||
| for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | |||||
| /* only editable and visible layers are considered */ | |||||
| if (gpencil_layer_is_editable(gpl)) { | |||||
| for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | |||||
| for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | |||||
| /* check if it is editable */ | |||||
| if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { | |||||
| continue; | |||||
| } | |||||
| gps_ma = give_current_material(ob, gps->mat_nr + 1); | |||||
| /* update */ | |||||
| if ((gps_ma) && (gps_ma == mat)) { | |||||
| ED_gpencil_calc_stroke_uv(ob, gps); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| /* ******************************************************** */ | /* ******************************************************** */ | ||||