diff options
author | Dalai Felinto <dfelinto@gmail.com> | 2016-10-20 20:26:16 +0300 |
---|---|---|
committer | Dalai Felinto <dfelinto@gmail.com> | 2016-10-20 20:26:16 +0300 |
commit | d0989e538effc1c8b5e948224beaad25ab4bd569 (patch) | |
tree | a4ee58fea6f190943cbb79d1985be66a779ae847 /source/blender/editors | |
parent | 418b24551e1b8a4458deceedca945a631f0684ac (diff) | |
parent | 7830ec54186e8b05a366775e02c6457eb83814a3 (diff) |
Merge commit '7830ec54186e8b05a366775e02c6457eb83814a3' into pbr-merge
Conflicts:
source/blender/nodes/shader/nodes/node_shader_texture.c
Diffstat (limited to 'source/blender/editors')
143 files changed, 7086 insertions, 2466 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index ea2f7fc5588..752544f65e1 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -4208,6 +4208,8 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle offset += ICON_WIDTH; } else if (ale->type == ANIMTYPE_GPLAYER) { +#if 0 + /* XXX: Maybe need a better design */ /* color swatch for layer color */ bGPDlayer *gpl = (bGPDlayer *)ale->data; PointerRNA ptr; @@ -4216,7 +4218,6 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle RNA_pointer_create(ale->id, &RNA_GPencilLayer, ale->data, &ptr); UI_block_align_begin(block); - UI_block_emboss_set(block, RNA_boolean_get(&ptr, "is_stroke_visible") ? UI_EMBOSS : UI_EMBOSS_NONE); uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset, yminc, w, ICON_WIDTH, &ptr, "color", -1, @@ -4226,11 +4227,11 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset + w, yminc, w, ICON_WIDTH, &ptr, "fill_color", -1, 0, 0, 0, 0, gpl->info); - UI_block_emboss_set(block, UI_EMBOSS_NONE); UI_block_align_end(block); - + offset += ICON_WIDTH; +#endif } } diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index c98470fb194..af9b0a176f5 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -2356,7 +2356,7 @@ static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selec } else { ymin = 0.0f; - ymax = (float)(-ACHANNEL_HEIGHT); + ymax = (float)(-ACHANNEL_HEIGHT(ac)); } /* convert border-region to view coordinates */ @@ -2372,7 +2372,7 @@ static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selec if (ac->datatype == ANIMCONT_NLA) ymin = ymax - NLACHANNEL_STEP(snla); else - ymin = ymax - ACHANNEL_STEP; + ymin = ymax - ACHANNEL_STEP(ac); /* if channel is within border-select region, alter it */ if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) { @@ -2573,7 +2573,7 @@ static int animchannels_channel_get(bAnimContext *ac, const int mval[2]) UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index); } else { - UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index); + UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP(ac), 0, (float)ACHANNEL_HEIGHT_HALF(ac), x, y, NULL, &channel_index); } return channel_index; @@ -2705,6 +2705,10 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, if ((adt) && (adt->flag & ADT_UI_SELECTED)) adt->flag |= ADT_UI_ACTIVE; + /* ensure we exit editmode on whatever object was active before to avoid getting stuck there - T48747 */ + if (ob != sce->obedit) + ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO); + notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); break; } @@ -2988,7 +2992,7 @@ static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmE * ACHANNEL_HEIGHT_HALF. */ UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y); - UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index); + UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP(&ac), 0, (float)ACHANNEL_HEIGHT_HALF(&ac), x, y, NULL, &channel_index); /* handle mouse-click in the relevant channel then */ notifierFlags = mouse_anim_channels(C, &ac, channel_index, selectmode); diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 910e195173c..88d96c531e0 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -71,6 +71,7 @@ #include "DNA_world_types.h" #include "DNA_gpencil_types.h" #include "DNA_object_types.h" +#include "DNA_userdef_types.h" #include "MEM_guardedalloc.h" @@ -97,9 +98,30 @@ #include "ED_anim_api.h" #include "ED_markers.h" +#include "UI_resources.h" /* for TH_KEYFRAME_SCALE lookup */ + /* ************************************************************ */ /* Blender Context <-> Animation Context mapping */ +/* ----------- Private Stuff - General -------------------- */ + +/* Get vertical scaling factor (i.e. typically used for keyframe size) */ +static void animedit_get_yscale_factor(bAnimContext *ac) +{ + bTheme *btheme = UI_GetTheme(); + + /* grab scale factor directly from action editor setting + * NOTE: This theme setting doesn't have an ID, as it cannot be accessed normally + * since it is a float, and the theem settings methods can only handle chars. + */ + ac->yscale_fac = btheme->tact.keyframe_scale_fac; + + /* clamp to avoid problems with uninitialised values... */ + if (ac->yscale_fac < 0.1f) + ac->yscale_fac = 1.0f; + //printf("yscale_fac = %f\n", ac->yscale_fac); +} + /* ----------- Private Stuff - Action Editor ------------- */ /* Get shapekey data being edited (for Action Editor -> ShapeKey mode) */ @@ -352,6 +374,9 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac) ac->spacetype = (sa) ? sa->spacetype : 0; ac->regiontype = (ar) ? ar->regiontype : 0; + /* initialise default y-scale factor */ + animedit_get_yscale_factor(ac); + /* get data context info */ // XXX: if the below fails, try to grab this info from context instead... (to allow for scripting) return ANIM_animdata_context_getdata(ac); @@ -1255,7 +1280,7 @@ static size_t animfilter_action(bAnimContext *ac, ListBase *anim_data, bDopeShee /* don't include anything from this action if it is linked in from another file, * and we're getting stuff for editing... */ - if ((filter_mode & ANIMFILTER_FOREDIT) && (act->id.lib)) + if ((filter_mode & ANIMFILTER_FOREDIT) && ID_IS_LINKED_DATABLOCK(act)) return 0; /* do groups */ @@ -2705,11 +2730,97 @@ static size_t animdata_filter_dopesheet_scene(bAnimContext *ac, ListBase *anim_d return items; } + +/* Helper for animdata_filter_dopesheet() - For checking if an object should be included or not */ +static bool animdata_filter_base_is_ok(bDopeSheet *ads, Scene *scene, Base *base, int filter_mode) +{ + Object *ob = base->object; + + if (base->object == NULL) + return false; + + /* firstly, check if object can be included, by the following factors: + * - if only visible, must check for layer and also viewport visibility + * --> while tools may demand only visible, user setting takes priority + * as user option controls whether sets of channels get included while + * tool-flag takes into account collapsed/open channels too + * - if only selected, must check if object is selected + * - there must be animation data to edit (this is done recursively as we + * try to add the channels) + */ + if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) { + /* layer visibility - we check both object and base, since these may not be in sync yet */ + if ((scene->lay & (ob->lay | base->lay)) == 0) + return false; + + /* outliner restrict-flag */ + if (ob->restrictflag & OB_RESTRICT_VIEW) + return false; + } + + /* if only F-Curves with visible flags set can be shown, check that + * datablock hasn't been set to invisible + */ + if (filter_mode & ANIMFILTER_CURVE_VISIBLE) { + if ((ob->adt) && (ob->adt->flag & ADT_CURVES_NOT_VISIBLE)) + return false; + } + + /* check selection and object type filters */ + if ((ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & SELECT) /*|| (base == sce->basact)*/)) { + /* only selected should be shown */ + return false; + } + + /* check if object belongs to the filtering group if option to filter + * objects by the grouped status is on + * - used to ease the process of doing multiple-character choreographies + */ + if (ads->filterflag & ADS_FILTER_ONLYOBGROUP) { + if (BKE_group_object_exists(ads->filter_grp, ob) == 0) + return false; + } + + /* no reason to exclude this object... */ + return true; +} + +/* Helper for animdata_filter_ds_sorted_bases() - Comparison callback for two Base pointers... */ +static int ds_base_sorting_cmp(const void *base1_ptr, const void *base2_ptr) +{ + const Base *b1 = *((const Base **)base1_ptr); + const Base *b2 = *((const Base **)base2_ptr); + + return strcmp(b1->object->id.name + 2, b2->object->id.name + 2); +} + +/* Get a sorted list of all the bases - for inclusion in dopesheet (when drawing channels) */ +static Base **animdata_filter_ds_sorted_bases(bDopeSheet *ads, Scene *scene, int filter_mode, size_t *r_usable_bases) +{ + /* Create an array with space for all the bases, but only containing the usable ones */ + size_t tot_bases = BLI_listbase_count(&scene->base); + size_t num_bases = 0; + + Base **sorted_bases = MEM_mallocN(sizeof(Base *) * tot_bases, "Dopesheet Usable Sorted Bases"); + for (Base *base = scene->base.first; base; base = base->next) { + if (animdata_filter_base_is_ok(ads, scene, base, filter_mode)) { + sorted_bases[num_bases++] = base; + } + } + + /* Sort this list of pointers (based on the names) */ + qsort(sorted_bases, num_bases, sizeof(Base *), ds_base_sorting_cmp); + + /* Return list of sorted bases */ + *r_usable_bases = num_bases; + return sorted_bases; +} + + // TODO: implement pinning... (if and when pinning is done, what we need to do is to provide freeing mechanisms - to protect against data that was deleted) static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, int filter_mode) { - Scene *sce = (Scene *)ads->source; - Base *base; + Scene *scene = (Scene *)ads->source; size_t items = 0; /* check that we do indeed have a scene */ @@ -2729,56 +2840,45 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, b } /* scene-linked animation - e.g. world, compositing nodes, scene anim (including sequencer currently) */ - items += animdata_filter_dopesheet_scene(ac, anim_data, ads, sce, filter_mode); - - /* loop over all bases (i.e.objects) in the scene */ - for (base = sce->base.first; base; base = base->next) { - /* check if there's an object (all the relevant checks are done in the ob-function) */ - if (base->object) { - Object *ob = base->object; - - /* firstly, check if object can be included, by the following factors: - * - if only visible, must check for layer and also viewport visibility - * --> while tools may demand only visible, user setting takes priority - * as user option controls whether sets of channels get included while - * tool-flag takes into account collapsed/open channels too - * - if only selected, must check if object is selected - * - there must be animation data to edit (this is done recursively as we - * try to add the channels) - */ - if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) { - /* layer visibility - we check both object and base, since these may not be in sync yet */ - if ((sce->lay & (ob->lay | base->lay)) == 0) continue; - - /* outliner restrict-flag */ - if (ob->restrictflag & OB_RESTRICT_VIEW) continue; - } - - /* if only F-Curves with visible flags set can be shown, check that - * datablock hasn't been set to invisible - */ - if (filter_mode & ANIMFILTER_CURVE_VISIBLE) { - if ((ob->adt) && (ob->adt->flag & ADT_CURVES_NOT_VISIBLE)) - continue; + items += animdata_filter_dopesheet_scene(ac, anim_data, ads, scene, filter_mode); + + /* If filtering for channel drawing, we want the objects in alphabetical order, + * to make it easier to predict where items are in the hierarchy + * - This order only really matters if we need to show all channels in the list (e.g. for drawing) + * (XXX: What about lingering "active" flags? The order may now become unpredictable) + * - Don't do this if this behaviour has been turned off (i.e. due to it being too slow) + * - Don't do this if there's just a single object + */ + if ((filter_mode & ANIMFILTER_LIST_CHANNELS) && !(ads->flag & ADS_FLAG_NO_DB_SORT) && + (scene->base.first != scene->base.last)) + { + /* Filter list of bases (i.e. objects), sort them, then add their contents normally... */ + // TODO: Cache the old sorted order - if the set of bases hasn't changed, don't re-sort... + Base **sorted_bases; + size_t num_bases; + + sorted_bases = animdata_filter_ds_sorted_bases(ads, scene, filter_mode, &num_bases); + if (sorted_bases) { + /* Add the necessary channels for these bases... */ + for (size_t i = 0; i < num_bases; i++) { + items += animdata_filter_dopesheet_ob(ac, anim_data, ads, sorted_bases[i], filter_mode); } - /* check selection and object type filters */ - if ( (ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & SELECT) /*|| (base == sce->basact)*/) ) { - /* only selected should be shown */ - continue; - } + // TODO: store something to validate whether any changes are needed? - /* check if object belongs to the filtering group if option to filter - * objects by the grouped status is on - * - used to ease the process of doing multiple-character choreographies - */ - if (ads->filterflag & ADS_FILTER_ONLYOBGROUP) { - if (BKE_group_object_exists(ads->filter_grp, ob) == 0) - continue; + /* free temporary data */ + MEM_freeN(sorted_bases); + } + } + else { + /* Filter and add contents of each base (i.e. object) without them sorting first + * NOTE: This saves performance in cases where order doesn't matter + */ + for (Base *base = scene->base.first; base; base = base->next) { + if (animdata_filter_base_is_ok(ads, scene, base, filter_mode)) { + /* since we're still here, this object should be usable */ + items += animdata_filter_dopesheet_ob(ac, anim_data, ads, base, filter_mode); } - - /* since we're still here, this object should be usable */ - items += animdata_filter_dopesheet_ob(ac, anim_data, ads, base, filter_mode); } } diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 6f1883cff55..6e776953356 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -40,6 +40,7 @@ #include "MEM_guardedalloc.h" #include "BLI_dlrbTree.h" +#include "BLI_math.h" #include "BLI_utildefines.h" #include "DNA_anim_types.h" @@ -282,6 +283,9 @@ static ActKeyBlock *bezts_to_new_actkeyblock(BezTriple *prev, BezTriple *beztn) ab->sel = (BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn)) ? SELECT : 0; ab->modified = 1; + if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) + ab->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD; + return ab; } @@ -305,16 +309,28 @@ static void add_bezt_to_keyblocks_list(DLRBT_Tree *blocks, BezTriple *first_bezt } - /* check if block needed - same value(s)? - * -> firstly, handles must have same central value as each other - * -> secondly, handles which control that section of the curve must be constant - */ + /* check if block needed */ if (prev == NULL) return; - if (IS_EQF(beztn->vec[1][1], prev->vec[1][1]) == 0) return; - - if (IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) == 0) return; - if (IS_EQF(prev->vec[1][1], prev->vec[2][1]) == 0) return; + if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) { + /* Animator tagged a "moving hold" + * - Previous key must also be tagged as a moving hold, otherwise + * we're just dealing with the first of a pair, and we don't + * want to be creating any phantom holds... + */ + if (BEZKEYTYPE(prev) != BEZT_KEYTYPE_MOVEHOLD) + return; + } + else { + /* Check for same values... + * - Handles must have same central value as each other + * - Handles which control that section of the curve must be constant + */ + if (IS_EQF(beztn->vec[1][1], prev->vec[1][1]) == 0) return; + + if (IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) == 0) return; + if (IS_EQF(prev->vec[1][1], prev->vec[2][1]) == 0) return; + } /* if there are no blocks already, just add as root */ if (blocks->root == NULL) { @@ -340,7 +356,13 @@ static void add_bezt_to_keyblocks_list(DLRBT_Tree *blocks, BezTriple *first_bezt */ if (IS_EQT(ab->start, prev->vec[1][0], BEZT_BINARYSEARCH_THRESH)) { /* set selection status and 'touched' status */ - if (BEZT_ISSEL_ANY(beztn)) ab->sel = SELECT; + if (BEZT_ISSEL_ANY(beztn)) + ab->sel = SELECT; + + /* XXX: only when the first one was a moving hold? */ + if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) + ab->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD; + ab->modified++; /* done... no need to insert */ @@ -485,7 +507,27 @@ void draw_keyframe_shape(float x, float y, float xscale, float hsize, short sel, /* tweak size of keyframe shape according to type of keyframe * - 'proper' keyframes have key_type = 0, so get drawn at full size */ - hsize -= 0.5f * key_type; + switch (key_type) { + case BEZT_KEYTYPE_KEYFRAME: /* must be full size */ + break; + + case BEZT_KEYTYPE_BREAKDOWN: /* slightly smaller than normal keyframe */ + hsize *= 0.85f; + break; + + case BEZT_KEYTYPE_MOVEHOLD: /* slightly smaller than normal keyframes (but by less than for breakdowns) */ + //hsize *= 0.72f; + hsize *= 0.95f; + break; + + case BEZT_KEYTYPE_EXTREME: /* slightly larger */ + hsize *= 1.2f; + break; + + default: + hsize -= 0.5f * key_type; + break; + } /* adjust view transform before starting */ glTranslatef(x, y, 0.0f); @@ -518,6 +560,15 @@ void draw_keyframe_shape(float x, float y, float xscale, float hsize, short sel, else UI_GetThemeColor4fv(TH_KEYTYPE_JITTER, inner_col); break; } + case BEZT_KEYTYPE_MOVEHOLD: /* similar to traditional keyframes, but different... */ + { + /* XXX: Should these get their own theme options instead? */ + if (sel) UI_GetThemeColorShade4fv(TH_STRIP_SELECT, 35, inner_col); + else UI_GetThemeColorShade4fv(TH_STRIP, 50, inner_col); + + inner_col[3] = 1.0f; /* full opacity, to avoid problems with visual glitches */ + break; + } case BEZT_KEYTYPE_KEYFRAME: /* traditional yellowish frames (default theme) */ default: { @@ -557,13 +608,16 @@ void draw_keyframe_shape(float x, float y, float xscale, float hsize, short sel, glTranslatef(-x, -y, 0.0f); } -static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, float ypos, short channelLocked) +static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, float ypos, float yscale_fac, bool channelLocked) { ActKeyColumn *ak; ActKeyBlock *ab; float alpha; float xscale; - float iconsize = U.widget_unit / 4.0f; + + const float iconsize = (U.widget_unit / 4.0f) * yscale_fac; + const float mhsize = iconsize * 0.7f; + glEnable(GL_BLEND); /* get View2D scaling factor */ @@ -576,6 +630,7 @@ static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, floa /* draw keyblocks */ if (blocks) { float sel_color[4], unsel_color[4]; + float sel_mhcol[4], unsel_mhcol[4]; /* cache colours first */ UI_GetThemeColor4fv(TH_STRIP_SELECT, sel_color); @@ -584,16 +639,32 @@ static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, floa sel_color[3] *= alpha; unsel_color[3] *= alpha; + copy_v4_v4(sel_mhcol, sel_color); + sel_mhcol[3] *= 0.8f; + copy_v4_v4(unsel_mhcol, unsel_color); + unsel_mhcol[3] *= 0.8f; + /* NOTE: the tradeoff for changing colors between each draw is dwarfed by the cost of checking validity */ for (ab = blocks->first; ab; ab = ab->next) { if (actkeyblock_is_valid(ab, keys)) { - /* draw block */ - if (ab->sel) - glColor4fv(sel_color); - else - glColor4fv(unsel_color); - - glRectf(ab->start, ypos - iconsize, ab->end, ypos + iconsize); + if (ab->flag & ACTKEYBLOCK_FLAG_MOVING_HOLD) { + /* draw "moving hold" long-keyframe block - slightly smaller */ + if (ab->sel) + glColor4fv(sel_mhcol); + else + glColor4fv(unsel_mhcol); + + glRectf(ab->start, ypos - mhsize, ab->end, ypos + mhsize); + } + else { + /* draw standard long-keyframe block */ + if (ab->sel) + glColor4fv(sel_color); + else + glColor4fv(unsel_color); + + glRectf(ab->start, ypos - iconsize, ab->end, ypos + iconsize); + } } } } @@ -619,7 +690,7 @@ static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, floa /* *************************** Channel Drawing Funcs *************************** */ -void draw_summary_channel(View2D *v2d, bAnimContext *ac, float ypos) +void draw_summary_channel(View2D *v2d, bAnimContext *ac, float ypos, float yscale_fac) { DLRBT_Tree keys, blocks; @@ -631,13 +702,13 @@ void draw_summary_channel(View2D *v2d, bAnimContext *ac, float ypos) BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); - draw_keylist(v2d, &keys, &blocks, ypos, 0); + draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, false); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } -void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos) +void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos, float yscale_fac) { DLRBT_Tree keys, blocks; @@ -649,13 +720,13 @@ void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos) BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); - draw_keylist(v2d, &keys, &blocks, ypos, 0); + draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, false); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } -void draw_object_channel(View2D *v2d, bDopeSheet *ads, Object *ob, float ypos) +void draw_object_channel(View2D *v2d, bDopeSheet *ads, Object *ob, float ypos, float yscale_fac) { DLRBT_Tree keys, blocks; @@ -667,19 +738,19 @@ void draw_object_channel(View2D *v2d, bDopeSheet *ads, Object *ob, float ypos) BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); - draw_keylist(v2d, &keys, &blocks, ypos, 0); + draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, false); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } -void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos) +void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos, float yscale_fac) { DLRBT_Tree keys, blocks; - short locked = (fcu->flag & FCURVE_PROTECTED) || - ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) || - ((adt && adt->action) && (adt->action->id.lib)); + bool locked = (fcu->flag & FCURVE_PROTECTED) || + ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) || + ((adt && adt->action) && ID_IS_LINKED_DATABLOCK(adt->action)); BLI_dlrbTree_init(&keys); BLI_dlrbTree_init(&blocks); @@ -689,18 +760,18 @@ void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos) BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); - draw_keylist(v2d, &keys, &blocks, ypos, locked); + draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, locked); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } -void draw_agroup_channel(View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos) +void draw_agroup_channel(View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos, float yscale_fac) { DLRBT_Tree keys, blocks; - short locked = (agrp->flag & AGRP_PROTECTED) || - ((adt && adt->action) && (adt->action->id.lib)); + bool locked = (agrp->flag & AGRP_PROTECTED) || + ((adt && adt->action) && ID_IS_LINKED_DATABLOCK(adt->action)); BLI_dlrbTree_init(&keys); BLI_dlrbTree_init(&blocks); @@ -710,17 +781,17 @@ void draw_agroup_channel(View2D *v2d, AnimData *adt, bActionGroup *agrp, float y BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); - draw_keylist(v2d, &keys, &blocks, ypos, locked); + draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, locked); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } -void draw_action_channel(View2D *v2d, AnimData *adt, bAction *act, float ypos) +void draw_action_channel(View2D *v2d, AnimData *adt, bAction *act, float ypos, float yscale_fac) { DLRBT_Tree keys, blocks; - short locked = (act && act->id.lib != NULL); + bool locked = (act && ID_IS_LINKED_DATABLOCK(act)); BLI_dlrbTree_init(&keys); BLI_dlrbTree_init(&blocks); @@ -730,13 +801,13 @@ void draw_action_channel(View2D *v2d, AnimData *adt, bAction *act, float ypos) BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); - draw_keylist(v2d, &keys, &blocks, ypos, locked); + draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, locked); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } -void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos) +void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos, float yscale_fac) { DLRBT_Tree keys; @@ -746,38 +817,42 @@ void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos BLI_dlrbTree_linkedlist_sync(&keys); - draw_keylist(v2d, &keys, NULL, ypos, 0); + draw_keylist(v2d, &keys, NULL, ypos, yscale_fac, false); BLI_dlrbTree_free(&keys); } -void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos) +void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos, float yscale_fac) { DLRBT_Tree keys; + bool locked = (gpl->flag & GP_LAYER_LOCKED) != 0; + BLI_dlrbTree_init(&keys); gpl_to_keylist(ads, gpl, &keys); BLI_dlrbTree_linkedlist_sync(&keys); - draw_keylist(v2d, &keys, NULL, ypos, (gpl->flag & GP_LAYER_LOCKED)); + draw_keylist(v2d, &keys, NULL, ypos, yscale_fac, locked); BLI_dlrbTree_free(&keys); } -void draw_masklay_channel(View2D *v2d, bDopeSheet *ads, MaskLayer *masklay, float ypos) +void draw_masklay_channel(View2D *v2d, bDopeSheet *ads, MaskLayer *masklay, float ypos, float yscale_fac) { DLRBT_Tree keys; - + + bool locked = (masklay->flag & MASK_LAYERFLAG_LOCKED) != 0; + BLI_dlrbTree_init(&keys); - + mask_to_keylist(ads, masklay, &keys); - + BLI_dlrbTree_linkedlist_sync(&keys); - - draw_keylist(v2d, &keys, NULL, ypos, (masklay->flag & MASK_LAYERFLAG_LOCKED)); - + + draw_keylist(v2d, &keys, NULL, ypos, yscale_fac, locked); + BLI_dlrbTree_free(&keys); } diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index 7b35a154fc8..4571df0f077 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -1208,6 +1208,13 @@ static short set_keytype_jitter(KeyframeEditData *UNUSED(ked), BezTriple *bezt) return 0; } +static short set_keytype_moving_hold(KeyframeEditData *UNUSED(ked), BezTriple *bezt) +{ + if (bezt->f2 & SELECT) + BEZKEYTYPE(bezt) = BEZT_KEYTYPE_MOVEHOLD; + return 0; +} + /* Set the interpolation type of the selected BezTriples in each F-Curve to the specified one */ KeyframeEditFunc ANIM_editkeyframes_keytype(short code) { @@ -1221,6 +1228,9 @@ KeyframeEditFunc ANIM_editkeyframes_keytype(short code) case BEZT_KEYTYPE_JITTER: /* jitter keyframe */ return set_keytype_jitter; + case BEZT_KEYTYPE_MOVEHOLD: /* moving hold */ + return set_keytype_moving_hold; + case BEZT_KEYTYPE_KEYFRAME: /* proper keyframe */ default: return set_keytype_keyframe; diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 0c0f54f0179..98be77b491f 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -930,11 +930,18 @@ bool insert_keyframe_direct(ReportList *reports, PointerRNA ptr, PropertyRNA *pr /* update F-Curve flags to ensure proper behaviour for property type */ update_autoflags_fcurve_direct(fcu, prop); - + /* adjust frame on which to add keyframe */ if ((flag & INSERTKEY_DRIVER) && (fcu->driver)) { - /* for making it easier to add corrective drivers... */ - cfra = evaluate_driver(fcu->driver, cfra); + PathResolvedRNA anim_rna; + + if (RNA_path_resolved_create(&ptr, prop, fcu->array_index, &anim_rna)) { + /* for making it easier to add corrective drivers... */ + cfra = evaluate_driver(&anim_rna, fcu->driver, cfra); + } + else { + cfra = 0.0f; + } } /* obtain value to give keyframe */ diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 6306926e0b2..6979a324b69 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -424,7 +424,9 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone for (curBone = bones->first; curBone; curBone = curBone->next) { eBone = MEM_callocN(sizeof(EditBone), "make_editbone"); - /* Copy relevant data from bone to eBone */ + /* Copy relevant data from bone to eBone + * Keep selection logic in sync with ED_armature_sync_selection. + */ eBone->parent = parent; BLI_strncpy(eBone->name, curBone->name, sizeof(eBone->name)); eBone->flag = curBone->flag; @@ -435,11 +437,11 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone eBone->flag |= BONE_TIPSEL; if (eBone->parent && (eBone->flag & BONE_CONNECTED)) { eBone->parent->flag |= BONE_TIPSEL; - eBone->flag &= ~BONE_ROOTSEL; /* this is ignored when there is a connected parent, so unset it */ - } - else { - eBone->flag |= BONE_ROOTSEL; } + + /* For connected bones, take care when changing the selection when we have a connected parent, + * this flag is a copy of '(eBone->parent->flag & BONE_TIPSEL)'. */ + eBone->flag |= BONE_ROOTSEL; } else { /* if the bone is not selected, but connected to its parent diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index a929507929f..5015829f868 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -86,7 +86,7 @@ void ED_armature_enter_posemode(bContext *C, Base *base) ReportList *reports = CTX_wm_reports(C); Object *ob = base->object; - if (ob->id.lib) { + if (ID_IS_LINKED_DATABLOCK(ob)) { BKE_report(reports, RPT_WARNING, "Cannot pose libdata"); return; } diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index dca9aa3e446..d9a3efa765c 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -183,7 +183,7 @@ static int has_poselib_pose_data_poll(bContext *C) static int has_poselib_pose_data_for_editing_poll(bContext *C) { Object *ob = get_poselib_object(C); - return (ob && ob->poselib && !ob->poselib->id.lib); + return (ob && ob->poselib && !ID_IS_LINKED_DATABLOCK(ob->poselib)); } /* ----------------------------------- */ @@ -385,7 +385,7 @@ static int poselib_add_poll(bContext *C) if (ED_operator_posemode(C)) { Object *ob = get_poselib_object(C); if (ob) { - if ((ob->poselib == NULL) || (ob->poselib->id.lib == 0)) { + if ((ob->poselib == NULL) || !ID_IS_LINKED_DATABLOCK(ob->poselib)) { return true; } } diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 420f72fedb3..e40dde24ce2 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1301,7 +1301,7 @@ static int separate_exec(bContext *C, wmOperator *op) DAG_relations_tag_update(bmain); newob = newbase->object; - newcu = newob->data = BKE_curve_copy(oldcu); + newcu = newob->data = BKE_curve_copy(bmain, oldcu); newcu->editnurb = NULL; id_us_min(&oldcu->id); /* because new curve is a copy: reduce user count */ @@ -5843,7 +5843,7 @@ static int curve_dissolve_exec(bContext *C, wmOperator *UNUSED(op)) normalize_v3(tan_r); curve_fit_cubic_to_points_single_fl( - points, points_len, dims, FLT_EPSILON, + points, points_len, NULL, dims, FLT_EPSILON, tan_l, tan_r, bezt_prev->vec[2], bezt_next->vec[0], &error_sq_dummy); @@ -6034,9 +6034,8 @@ int join_curve_exec(bContext *C, wmOperator *op) BLI_movelisttolist(&cu->nurb, &tempbase); DAG_relations_tag_update(bmain); // because we removed object(s), call before editmode! - - ED_object_editmode_enter(C, EM_WAITCURSOR); - ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR | EM_DO_UNDO); + + DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 38018541929..2d8fc76ee7e 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -55,6 +55,8 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" + #define USE_SPLINE_FIT #ifdef USE_SPLINE_FIT @@ -62,8 +64,11 @@ #endif /* Distance between input samples */ -#define STROKE_SAMPLE_DIST_MIN_PX 3 -#define STROKE_SAMPLE_DIST_MAX_PX 6 +#define STROKE_SAMPLE_DIST_MIN_PX 1 +#define STROKE_SAMPLE_DIST_MAX_PX 3 + +/* Distance between start/end points to consider cyclic */ +#define STROKE_CYCLIC_DIST_PX 8 /* -------------------------------------------------------------------- */ @@ -730,6 +735,11 @@ static void curve_draw_exec_precalc(wmOperator *op) const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings; PropertyRNA *prop; + prop = RNA_struct_find_property(op->ptr, "fit_method"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_enum_set(op->ptr, prop, cps->fit_method); + } + prop = RNA_struct_find_property(op->ptr, "corner_angle"); if (!RNA_property_is_set(op->ptr, prop)) { const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle : (float)M_PI; @@ -759,6 +769,32 @@ static void curve_draw_exec_precalc(wmOperator *op) RNA_property_float_set(op->ptr, prop, error_threshold); } + prop = RNA_struct_find_property(op->ptr, "use_cyclic"); + if (!RNA_property_is_set(op->ptr, prop)) { + bool use_cyclic = false; + + if (BLI_mempool_count(cdd->stroke_elem_pool) > 2) { + BLI_mempool_iter iter; + const struct StrokeElem *selem, *selem_first, *selem_last; + + BLI_mempool_iternew(cdd->stroke_elem_pool, &iter); + selem_first = BLI_mempool_iterstep(&iter); + for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) { + selem_last = selem; + } + + if (len_squared_v2v2( + selem_first->mval, + selem_last->mval) <= SQUARE(STROKE_CYCLIC_DIST_PX * U.pixelsize)) + { + use_cyclic = true; + } + } + + RNA_property_boolean_set(op->ptr, prop, use_cyclic); + } + + if ((cps->radius_taper_start != 0.0f) || (cps->radius_taper_end != 0.0f)) { @@ -868,8 +904,10 @@ static int curve_draw_exec(bContext *C, wmOperator *op) unsigned int cubic_spline_len = 0; /* error in object local space */ + const int fit_method = RNA_enum_get(op->ptr, "fit_method"); const float error_threshold = RNA_float_get(op->ptr, "error_threshold"); const float corner_angle = RNA_float_get(op->ptr, "corner_angle"); + const bool use_cyclic = RNA_boolean_get(op->ptr, "use_cyclic"); { BLI_mempool_iter iter; @@ -894,14 +932,14 @@ static int curve_draw_exec(bContext *C, wmOperator *op) unsigned int *corners = NULL; unsigned int corners_len = 0; - if (corner_angle < (float)M_PI) { + if ((fit_method == CURVE_PAINT_FIT_METHOD_SPLIT) && (corner_angle < (float)M_PI)) { /* this could be configurable... */ const float corner_radius_min = error_threshold / 8; const float corner_radius_max = error_threshold * 2; const unsigned int samples_max = 16; curve_fit_corners_detect_fl( - (const float *)coords, stroke_len, dims, + coords, stroke_len, dims, corner_radius_min, corner_radius_max, samples_max, corner_angle, &corners, &corners_len); @@ -909,13 +947,29 @@ static int curve_draw_exec(bContext *C, wmOperator *op) unsigned int *corners_index = NULL; unsigned int corners_index_len = 0; + unsigned int calc_flag = CURVE_FIT_CALC_HIGH_QUALIY; - const int result = curve_fit_cubic_to_points_fl( - coords, stroke_len, dims, error_threshold, CURVE_FIT_CALC_HIGH_QUALIY, - corners, corners_len, - &cubic_spline, &cubic_spline_len, - NULL, - &corners_index, &corners_index_len); + if ((stroke_len > 2) && use_cyclic) { + calc_flag |= CURVE_FIT_CALC_CYCLIC; + } + + int result; + if (fit_method == CURVE_PAINT_FIT_METHOD_REFIT) { + result = curve_fit_cubic_to_points_refit_fl( + coords, stroke_len, dims, error_threshold, calc_flag, + NULL, 0, corner_angle, + &cubic_spline, &cubic_spline_len, + NULL, + &corners_index, &corners_index_len); + } + else { + result = curve_fit_cubic_to_points_fl( + coords, stroke_len, dims, error_threshold, calc_flag, + corners, corners_len, + &cubic_spline, &cubic_spline_len, + NULL, + &corners_index, &corners_index_len); + } MEM_freeN(coords); if (corners) { @@ -950,11 +1004,24 @@ static int curve_draw_exec(bContext *C, wmOperator *op) if (corners_index) { /* ignore the first and last */ - for (unsigned int i = 1; i < corners_index_len - 1; i++) { + unsigned int i_start = 0, i_end = corners_index_len; + + if ((corners_index_len >= 2) && + (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0) + { + i_start += 1; + i_end -= 1; + } + + for (unsigned int i = i_start; i < i_end; i++) { bezt = &nu->bezt[corners_index[i]]; bezt->h1 = bezt->h2 = HD_FREE; } } + + if (calc_flag & CURVE_FIT_CALC_CYCLIC) { + nu->flagu |= CU_NURB_CYCLIC; + } } if (corners_index) { @@ -1220,13 +1287,19 @@ void CURVE_OT_draw(wmOperatorType *ot) 0.0001f, 10.0f); RNA_def_property_ui_range(prop, 0.0, 10, 1, 4); + RNA_def_enum(ot->srna, "fit_method", rna_enum_curve_fit_method_items, CURVE_PAINT_FIT_METHOD_REFIT, + "Fit Method", ""); + prop = RNA_def_float_distance( ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI); RNA_def_property_subtype(prop, PROP_ANGLE); - prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + prop = RNA_def_boolean(ot->srna, "use_cyclic", true, "Cyclic", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c index a29266294b4..cad70443657 100644 --- a/source/blender/editors/curve/editcurve_select.c +++ b/source/blender/editors/curve/editcurve_select.c @@ -294,11 +294,11 @@ static void select_adjacent_cp( if (next < 0) bezt = &nu->bezt[a - 1]; while (a--) { if (a - abs(next) < 0) break; - if ((lastsel == 0) && (bezt->hide == 0) && ((bezt->f2 & SELECT) || (selstatus == DESELECT))) { + if ((lastsel == false) && (bezt->hide == 0) && ((bezt->f2 & SELECT) || (selstatus == DESELECT))) { bezt += next; if (!(bezt->f2 & SELECT) || (selstatus == DESELECT)) { - short sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE); - if ((sel == 1) && (cont == 0)) lastsel = true; + bool sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE); + if (sel && !cont) lastsel = true; } } else { @@ -315,11 +315,11 @@ static void select_adjacent_cp( if (next < 0) bp = &nu->bp[a - 1]; while (a--) { if (a - abs(next) < 0) break; - if ((lastsel == 0) && (bp->hide == 0) && ((bp->f1 & SELECT) || (selstatus == DESELECT))) { + if ((lastsel == false) && (bp->hide == 0) && ((bp->f1 & SELECT) || (selstatus == DESELECT))) { bp += next; if (!(bp->f1 & SELECT) || (selstatus == DESELECT)) { - short sel = select_bpoint(bp, selstatus, SELECT, VISIBLE); - if ((sel == 1) && (cont == 0)) lastsel = true; + bool sel = select_bpoint(bp, selstatus, SELECT, VISIBLE); + if (sel && !cont) lastsel = true; } } else { @@ -820,7 +820,7 @@ static int select_less_exec(bContext *C, wmOperator *UNUSED(op)) BezTriple *bezt; int a; int sel = 0; - short lastsel = false; + bool lastsel = false; if (obedit->type == OB_SURF) { for (nu = editnurb->first; nu; nu = nu->next) { @@ -935,9 +935,8 @@ static int select_less_exec(bContext *C, wmOperator *UNUSED(op)) a = nu->pntsu * nu->pntsv; bp = nu->bp; while (a--) { - if ((lastsel == 0) && (bp->hide == 0) && (bp->f1 & SELECT)) { - if (lastsel != 0) sel = 1; - else sel = 0; + if ((lastsel == false) && (bp->hide == 0) && (bp->f1 & SELECT)) { + sel = 0; /* first and last are exceptions */ if (a == nu->pntsu * nu->pntsv - 1) { diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 053a7ee5023..b40b51e337f 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -1695,7 +1695,7 @@ static int font_open_exec(bContext *C, wmOperator *op) if (pprop->prop) { /* when creating new ID blocks, use is already 1, but RNA - * pointer se also increases user, so this compensates it */ + * pointer use also increases user, so this compensates it */ id_us_min(&font->id); RNA_id_pointer_create(&font->id, &idptr); diff --git a/source/blender/editors/curve/editfont_undo.c b/source/blender/editors/curve/editfont_undo.c index a0453f9694d..a61f863b61e 100644 --- a/source/blender/editors/curve/editfont_undo.c +++ b/source/blender/editors/curve/editfont_undo.c @@ -77,7 +77,7 @@ static struct { /* We could have the undo API pass in the previous state, for now store a local list */ ListBase local_links; -} uf_arraystore = {NULL}; +} uf_arraystore = {{NULL}}; /** * \param create: When false, only free the arrays. diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 79a2c494239..bd09616243b 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -52,6 +52,7 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_userdef_types.h" +#include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_global.h" @@ -94,9 +95,32 @@ typedef enum eDrawStrokeFlags { #define GP_DRAWTHICKNESS_SPECIAL 3 /* ----- Tool Buffer Drawing ------ */ +/* helper function to set color of buffer point */ +static void gp_set_tpoint_color(tGPspoint *pt, float ink[4]) +{ + float alpha = ink[3] * pt->strength; + CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); + glColor4f(ink[0], ink[1], ink[2], alpha); +} + +/* helper function to set color of point */ +static void gp_set_point_color(bGPDspoint *pt, float ink[4]) +{ + float alpha = ink[3] * pt->strength; + CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); + glColor4f(ink[0], ink[1], ink[2], alpha); +} + +/* helper function to set color and point */ +static void gp_set_color_and_tpoint(tGPspoint *pt, float ink[4]) +{ + gp_set_tpoint_color(pt, ink); + glVertex2iv(&pt->x); +} /* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */ -static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness, short dflag, short sflag) +static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness, + short dflag, short sflag, float ink[4]) { tGPspoint *pt; int i; @@ -113,7 +137,8 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn /* if drawing a single point, draw it larger */ glPointSize((float)(thickness + 2) * points->pressure); glBegin(GL_POINTS); - glVertex2iv(&points->x); + + gp_set_color_and_tpoint(points, ink); glEnd(); } else if (sflag & GP_STROKE_ERASER) { @@ -138,15 +163,18 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn glBegin(GL_LINE_STRIP); /* need to roll-back one point to ensure that there are no gaps in the stroke */ - if (i != 0) glVertex2iv(&(pt - 1)->x); + if (i != 0) { + gp_set_color_and_tpoint((pt - 1), ink); + } /* now the point we want... */ - glVertex2iv(&pt->x); + gp_set_color_and_tpoint(pt, ink); oldpressure = pt->pressure; } - else - glVertex2iv(&pt->x); + else { + gp_set_color_and_tpoint(pt, ink); + } } glEnd(); @@ -155,37 +183,35 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn } /* --------- 2D Stroke Drawing Helpers --------- */ - -/* helper function to calculate x-y drawing coordinates for 2D points */ -static void gp_calc_2d_stroke_xy(bGPDspoint *pt, short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) +/* change in parameter list */ +static void gp_calc_2d_stroke_fxy(float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) { if (sflag & GP_STROKE_2DSPACE) { - r_co[0] = pt->x; - r_co[1] = pt->y; + r_co[0] = pt[0]; + r_co[1] = pt[1]; } else if (sflag & GP_STROKE_2DIMAGE) { - const float x = (float)((pt->x * winx) + offsx); - const float y = (float)((pt->y * winy) + offsy); - + const float x = (float)((pt[0] * winx) + offsx); + const float y = (float)((pt[1] * winy) + offsy); + r_co[0] = x; r_co[1] = y; } else { - const float x = (float)(pt->x / 100 * winx) + offsx; - const float y = (float)(pt->y / 100 * winy) + offsy; - + const float x = (float)(pt[0] / 100 * winx) + offsx; + const float y = (float)(pt[1] / 100 * winy) + offsy; + r_co[0] = x; r_co[1] = y; } } - /* ----------- Volumetric Strokes --------------- */ /* draw a 2D buffer stroke in "volumetric" style * NOTE: the stroke buffer doesn't have any coordinate offsets/transforms */ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, short thickness, - short dflag, short UNUSED(sflag)) + short dflag, short UNUSED(sflag), float ink[4]) { GLUquadricObj *qobj = gluNewQuadric(); float modelview[4][4]; @@ -216,6 +242,7 @@ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, s glLoadMatrixf((float *)modelview); /* draw the disk using the current state... */ + gp_set_tpoint_color(pt, ink); gluDisk(qobj, 0.0, pt->pressure * thickness, 32, 1); @@ -229,7 +256,8 @@ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, s /* draw a 2D strokes in "volumetric" style */ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, short thickness, short dflag, short sflag, - int offsx, int offsy, int winx, int winy) + int offsx, int offsy, int winx, int winy, + float diff_mat[4][4], float ink[4]) { GLUquadricObj *qobj = gluNewQuadric(); float modelview[4][4]; @@ -238,7 +266,7 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor bGPDspoint *pt; int i; - + float fpt[3]; /* HACK: We need a scale factor for the drawing in the image editor, * which seems to use 1 unit as it's maximum size, whereas everything @@ -256,10 +284,14 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor glPushMatrix(); for (i = 0, pt = points; i < totpoints; i++, pt++) { + /* color of point */ + gp_set_point_color(pt, ink); + /* set the transformed position */ float co[2]; - gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); translate_m4(modelview, co[0], co[1], 0.0f); glLoadMatrixf((float *)modelview); @@ -276,8 +308,9 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor } /* draw a 3D stroke in "volumetric" style */ -static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, short thickness, - short UNUSED(dflag), short UNUSED(sflag)) +static void gp_draw_stroke_volumetric_3d( + bGPDspoint *points, int totpoints, short thickness, + short UNUSED(dflag), short UNUSED(sflag), float diff_mat[4][4], float ink[4]) { GLUquadricObj *qobj = gluNewQuadric(); @@ -286,7 +319,7 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor bGPDspoint *pt; int i; - + float fpt[3]; /* Get the basic modelview matrix we use for performing calculations */ glGetFloatv(GL_MODELVIEW_MATRIX, (float *)base_modelview); @@ -305,8 +338,13 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor glPushMatrix(); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { + /* color of point */ + gp_set_point_color(pt, ink); + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply translation to base_modelview, so that the translated point is put in the right place */ - translate_m4(base_modelview, pt->x, pt->y, pt->z); + translate_m4(base_modelview, fpt[0], fpt[1], fpt[2]); /* copy the translation component to the billboard matrix we're going to use, * then reset the base matrix to the original values so that we can do the same @@ -378,9 +416,9 @@ static void gp_stroke_2d_flat(bGPDspoint *points, int totpoints, float(*points2d static void gp_triangulate_stroke_fill(bGPDstroke *gps) { BLI_assert(gps->totpoints >= 3); - gps->tot_triangles = gps->totpoints - 2; - + /* allocate memory for temporary areas */ + gps->tot_triangles = gps->totpoints - 2; unsigned int (*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, "GP Stroke temp triangulation"); float (*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points"); @@ -390,6 +428,8 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) gp_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction); BLI_polyfill_calc((const float(*)[2])points2d, (unsigned int)gps->totpoints, direction, (unsigned int(*)[3])tmp_triangles); + /* Number of triangles */ + gps->tot_triangles = gps->totpoints - 2; /* save triangulation data in stroke cache */ if (gps->tot_triangles > 0) { if (gps->triangles == NULL) { @@ -399,9 +439,7 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles); } - int i; - - for (i = 0; i < gps->tot_triangles; i++) { + for (int i = 0; i < gps->tot_triangles; i++) { bGPDtriangle *stroke_triangle = &gps->triangles[i]; stroke_triangle->v1 = tmp_triangles[i][0]; stroke_triangle->v2 = tmp_triangles[i][1]; @@ -428,21 +466,27 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) /* draw fills for shapes */ -static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short dflag, int offsx, int offsy, int winx, int winy) +static void gp_draw_stroke_fill( + bGPdata *gpd, bGPDstroke *gps, + int offsx, int offsy, int winx, int winy, float diff_mat[4][4]) { + bGPDpalettecolor *palcolor; + int i; + float fpt[3]; + BLI_assert(gps->totpoints >= 3); - + + palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + /* Triangulation fill if high quality flag is enabled */ - if (dflag & GP_DRAWDATA_HQ_FILL) { + if (palcolor->flag & PC_COLOR_HQ_FILL) { bGPDtriangle *stroke_triangle; bGPDspoint *pt; - int i; - + /* Calculate triangles cache for filling area (must be done only after changes) */ if ((gps->flag & GP_STROKE_RECALC_CACHES) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) { gp_triangulate_stroke_fill(gps); } - /* Draw all triangles for filling the polygon (cache must be calculated before) */ BLI_assert(gps->tot_triangles >= 1); glBegin(GL_TRIANGLES); @@ -450,32 +494,33 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short if (gps->flag & GP_STROKE_3DSPACE) { /* vertex 1 */ pt = &gps->points[stroke_triangle->v1]; - glVertex3fv(&pt->x); - + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); /* vertex 2 */ pt = &gps->points[stroke_triangle->v2]; - glVertex3fv(&pt->x); - + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); /* vertex 3 */ pt = &gps->points[stroke_triangle->v3]; - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - /* vertex 1 */ pt = &gps->points[stroke_triangle->v1]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); - /* vertex 2 */ pt = &gps->points[stroke_triangle->v2]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); - /* vertex 3 */ pt = &gps->points[stroke_triangle->v3]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } @@ -483,30 +528,31 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short } else { /* As an initial implementation, we use the OpenGL filled polygon drawing - * here since it's the easiest option to implement for this case. It does - * come with limitations (notably for concave shapes), though it works well - * enough for many simple situations. - * - * We keep this legacy implementation around despite now having the high quality - * fills, as this is necessary for keeping everything working nicely for files - * created using old versions of Blender which may have depended on the artifacts - * the old fills created. - */ + * here since it's the easiest option to implement for this case. It does + * come with limitations (notably for concave shapes), though it shouldn't + * be much of an issue in most cases. + * + * We keep this legacy implementation around despite now having the high quality + * fills, as this is necessary for keeping everything working nicely for files + * created using old versions of Blender which may have depended on the artifacts + * the old fills created. + */ bGPDspoint *pt; - int i; - + glBegin(GL_POLYGON); for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (gps->flag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } + glEnd(); } } @@ -514,23 +560,33 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short /* ----- Existing Strokes Drawing (3D and Point) ------ */ /* draw a given stroke - just a single dot (only one point) */ -static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dflag, short sflag, - int offsx, int offsy, int winx, int winy) +static void gp_draw_stroke_point( + bGPDspoint *points, short thickness, short dflag, short sflag, + int offsx, int offsy, int winx, int winy, float diff_mat[4][4], float ink[4]) { + float fpt[3]; + bGPDspoint *pt = &points[0]; + + /* color of point */ + gp_set_point_color(pt, ink); + /* set point thickness (since there's only one of these) */ glPointSize((float)(thickness + 2) * points->pressure); + /* get final position using parent matrix */ + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* draw point */ if (sflag & GP_STROKE_3DSPACE) { glBegin(GL_POINTS); - glVertex3fv(&points->x); + glVertex3fv(fpt); glEnd(); } else { float co[2]; /* get coordinates of point */ - gp_calc_2d_stroke_xy(points, sflag, offsx, offsy, winx, winy, co); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); /* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, simple dot looks ok * - also mandatory in if Image Editor 'image-based' dot @@ -559,16 +615,21 @@ static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dfla } /* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */ -static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug, short UNUSED(sflag)) +static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug, + short UNUSED(sflag), float diff_mat[4][4], float ink[4], bool cyclic) { - bGPDspoint *pt; + bGPDspoint *pt, *pt2; float curpressure = points[0].pressure; int i; - + float fpt[3]; + float cyclic_fpt[3]; + /* draw stroke curve */ glLineWidth(max_ff(curpressure * thickness, 1.0f)); glBegin(GL_LINE_STRIP); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { + gp_set_point_color(pt, ink); + /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP) * Note: we want more visible levels of pressures when thickness is bigger. @@ -580,15 +641,29 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness glBegin(GL_LINE_STRIP); /* need to roll-back one point to ensure that there are no gaps in the stroke */ - if (i != 0) glVertex3fv(&(pt - 1)->x); + if (i != 0) { + pt2 = pt - 1; + mul_v3_m4v3(fpt, diff_mat, &pt2->x); + glVertex3fv(fpt); + } /* now the point we want... */ - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); + } + /* saves first point to use in cyclic */ + if (i == 0) { + copy_v3_v3(cyclic_fpt, fpt); } } + /* if cyclic draw line to first point */ + if (cyclic) { + glVertex3fv(cyclic_fpt); + } glEnd(); /* draw debug points of curve on top? */ @@ -597,9 +672,12 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness glPointSize((float)(thickness + 2)); glBegin(GL_POINTS); - for (i = 0, pt = points; i < totpoints && pt; i++, pt++) - glVertex3fv(&pt->x); + for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); + } glEnd(); + } } @@ -607,7 +685,7 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness /* draw a given stroke in 2d */ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, - bool debug, int offsx, int offsy, int winx, int winy) + bool debug, int offsx, int offsy, int winx, int winy, float diff_mat[4][4], float ink[4]) { /* otherwise thickness is twice that of the 3D view */ float thickness = (float)thickness_s * 0.5f; @@ -625,6 +703,7 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness bGPDspoint *pt1, *pt2; float pm[2]; int i; + float fpt[3]; glShadeModel(GL_FLAT); glBegin(GL_QUADS); @@ -635,10 +714,13 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness float m1[2], m2[2]; /* gradient and normal */ float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ float pthick; /* thickness at segment point */ - + /* get x and y coordinates from points */ - gp_calc_2d_stroke_xy(pt1, sflag, offsx, offsy, winx, winy, s0); - gp_calc_2d_stroke_xy(pt2, sflag, offsx, offsy, winx, winy, s1); + mul_v3_m4v3(fpt, diff_mat, &pt1->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0); + + mul_v3_m4v3(fpt, diff_mat, &pt2->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1); /* calculate gradient and normal - 'angle'=(ny/nx) */ m1[1] = s1[1] - s0[1]; @@ -650,6 +732,9 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness /* always use pressure from first point here */ pthick = (pt1->pressure * thickness * scalefac); + /* color of point */ + gp_set_point_color(pt1, ink); + /* if the first segment, start of segment is segment's normal */ if (i == 0) { /* draw start cap first @@ -725,6 +810,9 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness /* for once, we use second point's pressure (otherwise it won't be drawn) */ pthick = (pt2->pressure * thickness * scalefac); + /* color of point */ + gp_set_point_color(pt2, ink); + /* calculate points for end of segment */ mt[0] = m2[0] * pthick; mt[1] = m2[1] * pthick; @@ -770,14 +858,15 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness if (debug) { bGPDspoint *pt; int i; - + float fpt[3]; + glPointSize((float)(thickness_s + 2)); glBegin(GL_POINTS); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { float co[2]; - - gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); glVertex2fv(co); } glEnd(); @@ -818,26 +907,45 @@ static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag) } /* draw a set of strokes */ -static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag, - bool debug, short lthick, const float color[4], const float fill_color[4]) +static void gp_draw_strokes( + bGPdata *gpd, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag, + bool debug, short lthick, const float opacity, const float tintcolor[4], + const bool onion, const bool custonion, float diff_mat[4][4]) { bGPDstroke *gps; - + float tcolor[4]; + float tfill[4]; + short sthickness; + float ink[4]; + for (gps = gpf->strokes.first; gps; gps = gps->next) { /* check if stroke can be drawn */ - if (gp_can_draw_stroke(gps, dflag) == false) + if (gp_can_draw_stroke(gps, dflag) == false) { continue; - + } + /* check if the color is visible */ + bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + if ((palcolor == NULL) || + (palcolor->flag & PC_COLOR_HIDE) || + /* if onion and ghost flag do not draw*/ + (onion && (palcolor->flag & PC_COLOR_ONIONSKIN))) + { + continue; + } + + /* calculate thickness */ + sthickness = gps->thickness + lthick; + /* check which stroke-drawer to use */ if (dflag & GP_DRAWDATA_ONLY3D) { const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); int mask_orig = 0; - + if (no_xray) { glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); glDepthMask(0); glEnable(GL_DEPTH_TEST); - + /* first arg is normally rv3d->dist, but this isn't * available here and seems to work quite well without */ bglPolygonOffset(1.0f, 1.0f); @@ -846,34 +954,65 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int glPolygonOffset(-1.0f, -1.0f); #endif } - + /* 3D Fill */ - if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { - glColor4fv(fill_color); - gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy); + //if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { + if (gps->totpoints >= 3) { + /* set color using palette, tint color and opacity */ + interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]); + tfill[3] = palcolor->fill[3] * opacity; + if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) { + if (!onion) { + glColor4fv(tfill); + } + else { + if (custonion) { + glColor4fv(tintcolor); + } + else { + ARRAY_SET_ITEMS(tfill, UNPACK3(palcolor->fill), tintcolor[3]); + glColor4fv(tfill); + } + } + gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat); + } } - + /* 3D Stroke */ - glColor4fv(color); - - if (dflag & GP_DRAWDATA_VOLUMETRIC) { + /* set color using palette, tint color and opacity */ + if (!onion) { + interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]); + tcolor[3] = palcolor->color[3] * opacity; + copy_v4_v4(ink, tcolor); + } + else { + if (custonion) { + copy_v4_v4(ink, tintcolor); + } + else { + ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity); + copy_v4_v4(ink, tcolor); + } + } + if (palcolor->flag & PC_COLOR_VOLUMETRIC) { /* volumetric stroke drawing */ - gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, lthick, dflag, gps->flag); + gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, diff_mat, ink); } else { /* 3D Lines - OpenGL primitives-based */ if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy, + diff_mat, ink); } else { - gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, debug, gps->flag); + gp_draw_stroke_3d(gps->points, gps->totpoints, sthickness, debug, gps->flag, + diff_mat, ink, gps->flag & GP_STROKE_CYCLIC); } } - if (no_xray) { glDepthMask(mask_orig); glDisable(GL_DEPTH_TEST); - + bglPolygonOffset(0.0, 0.0); #if 0 glDisable(GL_POLYGON_OFFSET_LINE); @@ -883,25 +1022,58 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int } else { /* 2D - Fill */ - if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { - glColor4fv(fill_color); - gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy); + if (gps->totpoints >= 3) { + /* set color using palette, tint color and opacity */ + interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]); + tfill[3] = palcolor->fill[3] * opacity; + if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) { + if (!onion) { + glColor4fv(tfill); + } + else { + if (custonion) { + glColor4fv(tintcolor); + } + else { + ARRAY_SET_ITEMS(tfill, palcolor->fill[0], palcolor->fill[1], palcolor->fill[2], + tintcolor[3]); + glColor4fv(tfill); + } + } + gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat); + } } - + /* 2D Strokes... */ - glColor4fv(color); - - if (dflag & GP_DRAWDATA_VOLUMETRIC) { + /* set color using palette, tint color and opacity */ + if (!onion) { + interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]); + tcolor[3] = palcolor->color[3] * opacity; + copy_v4_v4(ink, tcolor); + } + else { + if (custonion) { + copy_v4_v4(ink, tintcolor); + } + else { + ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity); + copy_v4_v4(ink, tcolor); + } + } + if (palcolor->flag & PC_COLOR_VOLUMETRIC) { /* blob/disk-based "volumetric" drawing */ - gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, + offsx, offsy, winx, winy, diff_mat, ink); } else { /* normal 2D strokes */ if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy, + diff_mat, ink); } else { - gp_draw_stroke_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, offsx, offsy, winx, winy); + gp_draw_stroke_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, debug, + offsx, offsy, winx, winy, diff_mat, ink); } } } @@ -909,13 +1081,19 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int } /* Draw selected verts for strokes being edited */ -static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag, const float tcolor[3]) +static void gp_draw_strokes_edit( + bGPdata *gpd, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag, + short lflag, float diff_mat[4][4], float alpha) { bGPDstroke *gps; + /* if alpha 0 do not draw */ + if (alpha == 0.0f) + return; + const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0; int mask_orig = 0; - + /* set up depth masks... */ if (dflag & GP_DRAWDATA_ONLY3D) { if (no_xray) { @@ -939,7 +1117,8 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, bGPDspoint *pt; float vsize, bsize; int i; - + float fpt[3]; + /* check if stroke can be drawn */ if (gp_can_draw_stroke(gps, dflag) == false) continue; @@ -951,6 +1130,19 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, if ((gps->flag & GP_STROKE_SELECT) == 0) continue; + /* verify palette color lock */ + { + bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + if (palcolor != NULL) { + if (palcolor->flag & PC_COLOR_HIDE) { + continue; + } + if (((lflag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) { + continue; + } + } + } + /* Get size of verts: * - The selected state needs to be larger than the unselected state so that * they stand out more. @@ -966,25 +1158,23 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, } /* First Pass: Draw all the verts (i.e. these become the unselected state) */ - if (tcolor != NULL) { - /* for now, we assume that the base color of the points is not too close to the real color */ - glColor3fv(tcolor); - } - else { - /* this doesn't work well with the default theme and black strokes... */ - UI_ThemeColor(TH_GP_VERTEX); - } + /* for now, we assume that the base color of the points is not too close to the real color */ + /* set color using palette */ + bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + glColor3fv(palcolor->color); + glPointSize(bsize); glBegin(GL_POINTS); for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) { if (gps->flag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } @@ -992,24 +1182,54 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, /* Second Pass: Draw only verts which are selected */ - UI_ThemeColor(TH_GP_VERTEX_SELECT); + float curColor[4]; + UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, curColor); + glColor4f(curColor[0], curColor[1], curColor[2], alpha); + glPointSize(vsize); glBegin(GL_POINTS); for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) { if (pt->flag & GP_SPOINT_SELECT) { if (gps->flag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } } glEnd(); + + /* Draw start and end point if enabled stroke direction hint */ + if ((gpd->flag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1)) { + bGPDspoint *p; + + glPointSize(vsize + 4); + glBegin(GL_POINTS); + + /* start point in green bigger */ + glColor3f(0.0f, 1.0f, 0.0f); + p = &gps->points[0]; + mul_v3_m4v3(fpt, diff_mat, &p->x); + glVertex3fv(fpt); + glEnd(); + + /* end point in red smaller */ + glPointSize(vsize + 1); + glBegin(GL_POINTS); + + glColor3f(1.0f, 0.0f, 0.0f); + p = &gps->points[gps->totpoints - 1]; + mul_v3_m4v3(fpt, diff_mat, &p->x); + glVertex3fv(fpt); + glEnd(); + } } @@ -1031,18 +1251,20 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, /* ----- General Drawing ------ */ /* draw onion-skinning for a layer */ -static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, - int UNUSED(cfra), int dflag, bool debug, short lthick) +static void gp_draw_onionskins( + bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, + int UNUSED(cfra), int dflag, bool debug, float diff_mat[4][4]) { - const float alpha = gpl->color[3]; + const float default_color[3] = {UNPACK3(U.gpencil_new_layer_col)}; + const float alpha = 1.0f; float color[4]; - + /* 1) Draw Previous Frames First */ if (gpl->flag & GP_LAYER_GHOST_PREVCOL) { copy_v3_v3(color, gpl->gcolor_prev); } else { - copy_v3_v3(color, gpl->color); + copy_v3_v3(color, default_color); } if (gpl->gstep > 0) { @@ -1056,7 +1278,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* alpha decreases with distance from curframe index */ fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1)); color[3] = alpha * fac * 0.66f; - gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat); } else break; @@ -1066,7 +1289,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* draw the strokes for the ghost frames (at half of the alpha set by user) */ if (gpf->prev) { color[3] = (alpha / 7); - gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gpf->prev, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat); } } else { @@ -1079,7 +1303,7 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of copy_v3_v3(color, gpl->gcolor_next); } else { - copy_v3_v3(color, gpl->color); + copy_v3_v3(color, default_color); } if (gpl->gstep_next > 0) { @@ -1093,7 +1317,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* alpha decreases with distance from curframe index */ fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1)); color[3] = alpha * fac * 0.66f; - gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat); } else break; @@ -1103,27 +1328,31 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* draw the strokes for the ghost frames (at half of the alpha set by user) */ if (gpf->next) { color[3] = (alpha / 4); - gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gpf->next, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat); } } else { /* don't draw - disabled */ } - /* 3) restore alpha */ - glColor4fv(gpl->color); } /* loop over gpencil data layers, drawing them */ -static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) +static void gp_draw_data_layers( + bGPDbrush *brush, float alpha, bGPdata *gpd, + int offsx, int offsy, int winx, int winy, int cfra, int dflag) { bGPDlayer *gpl; - + float diff_mat[4][4]; + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { bGPDframe *gpf; - + /* calculate parent position */ + ED_gpencil_parent_location(gpl, diff_mat); + bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? true : false; - short lthick = gpl->thickness; + short lthick = brush->thickness + gpl->thickness; /* don't draw layer if hidden */ if (gpl->flag & GP_LAYER_HIDE) @@ -1155,9 +1384,6 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in /* HQ fills... */ GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_HQ_FILL), GP_DRAWDATA_HQ_FILL); - /* fill strokes... */ - // XXX: this is not a very good limit - GP_DRAWFLAG_APPLY((gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH), GP_DRAWDATA_FILL); #undef GP_DRAWFLAG_APPLY /* draw 'onionskins' (frame left + right) */ @@ -1165,11 +1391,12 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in /* Drawing method - only immediately surrounding (gstep = 0), * or within a frame range on either side (gstep > 0) */ - gp_draw_onionskins(gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, lthick); + gp_draw_onionskins(gpd, gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, diff_mat); } /* draw the strokes already in active frame */ - gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, gpl->color, gpl->fill); + gp_draw_strokes(gpd, gpf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, + gpl->opacity, gpl->tintcolor, false, false, diff_mat); /* Draw verts of selected strokes * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering @@ -1183,8 +1410,7 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in (gpl->flag & GP_LAYER_LOCKED) == 0 && (gpd->flag & GP_DATA_STROKE_EDITMODE)) { - gp_draw_strokes_edit(gpf, offsx, offsy, winx, winy, dflag, - (gpl->color[3] < 0.95f) ? gpl->color : NULL); + gp_draw_strokes_edit(gpd, gpf, offsx, offsy, winx, winy, dflag, gpl->flag, diff_mat, alpha); } /* Check if may need to draw the active stroke cache, only if this layer is the active layer @@ -1194,7 +1420,7 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in (gpf->flag & GP_FRAME_PAINT)) { /* Set color for drawing buffer stroke - since this may not be set yet */ - glColor4fv(gpl->color); + // glColor4fv(gpl->color); /* Buffer stroke needs to be drawn with a different linestyle * to help differentiate them from normal strokes. @@ -1202,11 +1428,12 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in * It should also be noted that sbuffer contains temporary point types * i.e. tGPspoints NOT bGPDspoints */ - if (gpl->flag & GP_LAYER_VOLUMETRIC) { - gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag); + if (gpd->sflag & PC_COLOR_VOLUMETRIC) { + gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, + dflag, gpd->sbuffer_sflag, gpd->scolor); } else { - gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag); + gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag, gpd->scolor); } } } @@ -1258,7 +1485,9 @@ static void gp_draw_status_text(bGPdata *gpd, ARegion *ar) } /* draw grease-pencil datablock */ -static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) +static void gp_draw_data( + bGPDbrush *brush, float alpha, bGPdata *gpd, + int offsx, int offsy, int winx, int winy, int cfra, int dflag) { /* reset line drawing style (in case previous user didn't reset) */ setlinestyle(0); @@ -1276,7 +1505,7 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, glEnable(GL_BLEND); /* draw! */ - gp_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag); + gp_draw_data_layers(brush, alpha, gpd, offsx, offsy, winx, winy, cfra, dflag); /* turn off alpha blending, then smooth lines */ glDisable(GL_BLEND); // alpha blending @@ -1303,14 +1532,25 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i } if (gpd_source) { - gp_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag); + ToolSettings *ts = scene->toolsettings; + bGPDbrush *brush = gpencil_brush_getactive(ts); + if (brush != NULL) { + gp_draw_data(brush, ts->gp_sculpt.alpha, gpd_source, + offsx, offsy, winx, winy, cfra, dflag); + } + } } /* scene/clip data has already been drawn, only object/track data is drawn here * if gpd_source == gpd, we don't have any object/track data and we can skip */ if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) { - gp_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag); + ToolSettings *ts = scene->toolsettings; + bGPDbrush *brush = gpencil_brush_getactive(ts); + if (brush != NULL) { + gp_draw_data(brush, ts->gp_sculpt.alpha, gpd, + offsx, offsy, winx, winy, cfra, dflag); + } } } @@ -1479,6 +1719,7 @@ void ED_gpencil_draw_view3d(wmWindowManager *wm, Scene *scene, View3D *v3d, AReg /* draw it! */ gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); + } void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 0271afd6827..1bb3b7e1ae7 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2015, Blender Foundation * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** * @@ -51,6 +51,7 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_gpencil_types.h" +#include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_global.h" @@ -223,9 +224,26 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i GP_EditBrush_Data *brush = gso->brush; float inf = gp_brush_influence_calc(gso, radius, co); bool affect_pressure = (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) != 0; - + /* need one flag enabled by default */ + if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0) + { + gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; + } + /* perform smoothing */ - return gp_smooth_stroke(gps, i, inf, affect_pressure); + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) { + gp_smooth_stroke(gps, i, inf, affect_pressure); + } + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) { + gp_smooth_stroke_strength(gps, i, inf); + } + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) { + gp_smooth_stroke_thickness(gps, i, inf); + } + + return true; } /* ----------------------------------------------- */ @@ -268,6 +286,41 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in /* ----------------------------------------------- */ +/* Color Strength Brush */ + +/* Make color more or less transparent by the specified amounts */ +static bool gp_brush_strength_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int i, + const int radius, const int co[2]) +{ + bGPDspoint *pt = gps->points + i; + float inf; + + /* Compute strength of effect + * - We divide the strength by 10, so that users can set "sane" values. + * Otherwise, good default values are in the range of 0.093 + */ + inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; + + /* apply */ + // XXX: this is much too strong, and it should probably do some smoothing with the surrounding stuff + if (gp_brush_invert_check(gso)) { + /* make line thinner - reduce stroke pressure */ + pt->strength -= inf; + } + else { + /* make line thicker - increase stroke pressure */ + pt->strength += inf; + } + + /* Strength should stay within [0.0, 1.0] */ + CLAMP(pt->strength, 0.0f, 1.0f); + + return true; +} + + +/* ----------------------------------------------- */ /* Grab Brush */ /* Custom data per stroke for the Grab Brush @@ -373,11 +426,12 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) } /* Apply grab transform to all relevant points of the affected strokes */ -static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps) +static void gp_brush_grab_apply_cached( + tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, float diff_mat[4][4]) { tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); int i; - + /* Apply dvec to all of the stored points */ for (i = 0; i < data->size; i++) { bGPDspoint *pt = &gps->points[data->points[i]]; @@ -385,9 +439,23 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps) /* adjust the amount of displacement to apply */ mul_v3_v3fl(delta, gso->dvec, data->weights[i]); + if (!parented) { + /* apply */ + add_v3_v3(&pt->x, delta); + } + else { + float fpt[3]; + /* apply transformation */ + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply */ + add_v3_v3(fpt, delta); + copy_v3_v3(&pt->x, fpt); + /* undo transformation to the init parent position */ + float inverse_diff_mat[4][4]; + invert_m4_m4(inverse_diff_mat, diff_mat); + mul_m4_v3(inverse_diff_mat, &pt->x); + } - /* apply */ - add_v3_v3(&pt->x, delta); } } @@ -592,62 +660,92 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in */ const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; const float fac = BLI_frand() * inf; - - /* Jitter is applied perpendicular to the mouse movement vector - * - We compute all effects in screenspace (since it's easier) - * and then project these to get the points/distances in - * viewspace as needed - */ - float mvec[2], svec[2]; - - /* mouse movement in ints -> floats */ - mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - - /* rotate mvec by 90 degrees... */ - svec[0] = -mvec[1]; - svec[1] = mvec[0]; - - //printf("svec = %f %f, ", svec[0], svec[1]); - - /* scale the displacement by the random displacement, and apply */ - if (BLI_frand() > 0.5f) { - mul_v2_fl(svec, -fac); - } - else { - mul_v2_fl(svec, fac); + /* need one flag enabled by default */ + if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0) + { + gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; } - - //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]); - - /* convert to dataspace */ - if (gps->flag & GP_STROKE_3DSPACE) { - /* 3D: Project to 3D space */ - if (gso->sa->spacetype == SPACE_VIEW3D) { - bool flip; - RegionView3D *rv3d = gso->ar->regiondata; - float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); - if (flip == false) { - float dvec[3]; - ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); - add_v3_v3(&pt->x, dvec); + + /* apply random to position */ + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) { + /* Jitter is applied perpendicular to the mouse movement vector + * - We compute all effects in screenspace (since it's easier) + * and then project these to get the points/distances in + * viewspace as needed + */ + float mvec[2], svec[2]; + + /* mouse movement in ints -> floats */ + mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + /* rotate mvec by 90 degrees... */ + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + + /* scale the displacement by the random displacement, and apply */ + if (BLI_frand() > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + + //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]); + + /* convert to dataspace */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* 3D: Project to 3D space */ + if (gso->sa->spacetype == SPACE_VIEW3D) { + bool flip; + RegionView3D *rv3d = gso->ar->regiondata; + float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); + if (flip == false) { + float dvec[3]; + ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); + add_v3_v3(&pt->x, dvec); + } + } + else { + /* ERROR */ + BLI_assert("3D stroke being sculpted in non-3D view"); } } else { - /* ERROR */ - BLI_assert("3D stroke being sculpted in non-3D view"); + /* 2D: As-is */ + // XXX: v2d scaling/offset? + float nco[2]; + nco[0] = (float)co[0] + svec[0]; + nco[1] = (float)co[1] + svec[1]; + + copy_v2_v2(&pt->x, nco); } } - else { - /* 2D: As-is */ - // XXX: v2d scaling/offset? - float nco[2]; - nco[0] = (float)co[0] + svec[0]; - nco[1] = (float)co[1] + svec[1]; - - copy_v2_v2(&pt->x, nco); + /* apply random to strength */ + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) { + if (BLI_frand() > 0.5f) { + pt->strength += fac; + } + else { + pt->strength -= fac; + } + CLAMP_MIN(pt->strength, 0.0f); + CLAMP_MAX(pt->strength, 1.0f); } - + /* apply random to thickness (use pressure) */ + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) { + if (BLI_frand() > 0.5f) { + pt->pressure += fac; + } + else { + pt->pressure -= fac; + } + /* only limit lower value */ + CLAMP_MIN(pt->pressure, 0.0f); + } + /* done */ return true; } @@ -1099,7 +1197,9 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) /* Apply ----------------------------------------------- */ /* Apply brush operation to points in this stroke */ -static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP_BrushApplyCb apply) +static bool gpsculpt_brush_do_stroke( + tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, + float diff_mat[4][4], GP_BrushApplyCb apply) { GP_SpaceConversion *gsc = &gso->gsc; rcti *rect = &gso->brush_rect; @@ -1111,9 +1211,16 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP int i; bool include_last = false; bool changed = false; - + if (gps->totpoints == 1) { - gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]); + if (!parented) { + gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]); + } + else { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + } /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { @@ -1140,10 +1247,20 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP continue; } } - - gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]); - gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]); - + if (!parented) { + gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]); + gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]); + } + else { + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + } + + /* Check that point segment of the boundbox of the selection stroke */ if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) @@ -1228,76 +1345,105 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) /* Find visible strokes, and perform operations on those if hit */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + float diff_mat[4][4]; + bool parented = false; + + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - switch (gso->brush_type) { - case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_smooth_apply); - break; - } - - case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_thickness_apply); - break; + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + parented = true; + } + else { + parented = false; + } + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; } - - case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */ - { - if (gso->first) { - /* First time this brush stroke is being applied: - * 1) Prepare data buffers (init/clear) for this stroke - * 2) Use the points now under the cursor - */ - gp_brush_grab_stroke_init(gso, gps); - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_grab_store_points); + + switch (gso->brush_type) { + case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_smooth_apply); + break; } - else { - /* Apply effect to the stored points */ - gp_brush_grab_apply_cached(gso, gps); - changed |= true; + + case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_thickness_apply); + break; } - break; - } - - case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_push_apply); - break; - } - - case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_pinch_apply); - break; - } - - case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_twist_apply); - break; + + case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_strength_apply); + break; + } + + case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */ + { + if (gso->first) { + /* First time this brush stroke is being applied: + * 1) Prepare data buffers (init/clear) for this stroke + * 2) Use the points now under the cursor + */ + gp_brush_grab_stroke_init(gso, gps); + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_grab_store_points); + } + else { + /* Apply effect to the stored points */ + gp_brush_grab_apply_cached(gso, gps, parented, diff_mat); + changed |= true; + } + break; + } + + case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_push_apply); + break; + } + + case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_pinch_apply); + break; + } + + case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_twist_apply); + break; + } + + case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_randomize_apply); + break; + } + + default: + printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); + break; } - - case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_randomize_apply); - break; + /* Triangulation must be calculated if changed */ + if (changed) { + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; } - - default: - printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); - break; - } - - /* Triangulation must be calculated if changed */ - if (changed) { - gps->flag |= GP_STROKE_RECALC_CACHES; - gps->tot_triangles = 0; } } CTX_DATA_END; - + return changed; } @@ -1441,6 +1587,11 @@ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *eve needs_timer = true; break; + case GP_EDITBRUSH_TYPE_STRENGTH: + brush_rate = 0.01f; // XXX: hardcoded + needs_timer = true; + break; + case GP_EDITBRUSH_TYPE_PINCH: brush_rate = 0.001f; // XXX: hardcoded needs_timer = true; diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index c47985ebc1b..95ea13c399a 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -142,12 +142,31 @@ static EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), PointerRN /* convert the coordinates from the given stroke point into 3d-coordinates * - assumes that the active space is the 3D-View */ -static void gp_strokepoint_convertcoords(bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect) +static void gp_strokepoint_convertcoords( + bContext *C, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt, + float p3d[3], const rctf *subrect) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); ARegion *ar = CTX_wm_region(C); - + bGPDspoint mypt, *pt; + + float diff_mat[4][4]; + pt = &mypt; + + /* calculate difference matrix if parent object */ + if (gpl->parent == NULL) { + copy_v3_v3(&pt->x, &source_pt->x); + } + else { + /* apply parent transform */ + float fpt[3]; + ED_gpencil_parent_location(gpl, diff_mat); + mul_v3_m4v3(fpt, diff_mat, &source_pt->x); + copy_v3_v3(&pt->x, fpt); + } + + if (gps->flag & GP_STROKE_3DSPACE) { /* directly use 3d-coordinates */ copy_v3_v3(p3d, &pt->x); @@ -628,7 +647,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv bp = &nu->bp[old_nbp - 1]; /* First point */ - gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect); if (prev_bp) { interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC); if (do_gtd) { @@ -649,7 +668,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv /* Second point */ /* Note dt2 is always negative, which marks the gap. */ if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect); interp_v3_v3v3(p2, p, next_p, -GAP_DFAC); if (do_gtd) { dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); @@ -670,9 +689,9 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv float p[3], next_p[3]; float dt = 0.0f; - gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect); if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect); interp_v3_v3v3(p, p, next_p, -GAP_DFAC); if (do_gtd) { dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); @@ -701,10 +720,10 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv i++, pt++, bp++) { float p[3]; - float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; + float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC; /* get coordinates to add at */ - gp_strokepoint_convertcoords(C, gps, pt, p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt, p, subrect); gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights); @@ -816,12 +835,12 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu /* get initial coordinates */ pt = gps->points; if (tot) { - gp_strokepoint_convertcoords(C, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); if (tot > 1) { - gp_strokepoint_convertcoords(C, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); } if (stitch && tot > 2) { - gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect); } } @@ -940,7 +959,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu /* add points */ for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) { - float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; + float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC; if (i || old_nbezt) { interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC); @@ -964,7 +983,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu copy_v3_v3(p3d_cur, p3d_next); if (i + 2 < tot) { - gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect); } prev_bezt = bezt; diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 746497f0ff5..e915446e461 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** * @@ -40,6 +40,8 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_math.h" #include "BLT_translation.h" @@ -57,6 +59,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_colortools.h" #include "UI_interface.h" #include "UI_resources.h" @@ -72,6 +75,8 @@ #include "gpencil_intern.h" +/* maximum sizes of gp-session buffer */ +#define GP_STROKE_BUFFER_MAX 5000 /* ************************************************ */ /* Datablock Operators */ @@ -596,6 +601,61 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot) "In addition to toggling the editability, also affect the visibility"); } +/* ********************** Merge Layer with the next layer **************************** */ + +static int gp_merge_layer_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl_current = gpencil_layer_getactive(gpd); + bGPDlayer *gpl_next = gpl_current->next; + + if (ELEM(NULL, gpd, gpl_current, gpl_next)) { + BKE_report(op->reports, RPT_ERROR, "No layers to merge"); + return OPERATOR_CANCELLED; + } + + /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */ + GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64); + for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) { + BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf); + } + + /* read all frames from next layer */ + for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { + /* try to find frame in active layer */ + bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum)); + if (!frame) { + /* nothing found, create new */ + frame = gpencil_frame_addnew(gpl_current, gpf->framenum); + } + /* add to tail all strokes */ + BLI_movelisttolist(&frame->strokes, &gpf->strokes); + } + /* Now delete next layer */ + gpencil_layer_delete(gpd, gpl_next); + BLI_ghash_free(gh_frames_cur, NULL, NULL); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_merge(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Merge Down"; + ot->idname = "GPENCIL_OT_layer_merge"; + ot->description = "Merge the current layer with the layer below"; + + /* callbacks */ + ot->exec = gp_merge_layer_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ********************** Change Layer ***************************** */ static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) @@ -663,3 +723,1740 @@ void GPENCIL_OT_layer_change(wmOperatorType *ot) } /* ************************************************ */ + +/* ******************* Arrange Stroke Up/Down in drawing order ************************** */ + +enum { + GP_STROKE_MOVE_UP = -1, + GP_STROKE_MOVE_DOWN = 1, + GP_STROKE_MOVE_TOP = 2, + GP_STROKE_MOVE_BOTTOM = 3 +}; + +static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDstroke *gps; + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->actframe)) { + return OPERATOR_CANCELLED; + } + + bGPDframe *gpf = gpl->actframe; + /* temp listbase to store selected strokes */ + ListBase selected = {NULL}; + const int direction = RNA_enum_get(op->ptr, "type"); + + /* verify if any selected stroke is in the extreme of the stack and select to move */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + /* some stroke is already at front*/ + if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { + if (gps == gpf->strokes.last) { + BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on top"); + return OPERATOR_CANCELLED; + } + } + /* some stroke is already at botom */ + if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { + if (gps == gpf->strokes.first) { + BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on bottom"); + return OPERATOR_CANCELLED; + } + } + /* add to list */ + BLI_addtail(&selected, BLI_genericNodeN(gps)); + } + } + + /* Now do the movement of the stroke */ + switch (direction) { + /* Bring to Front */ + case GP_STROKE_MOVE_TOP: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkafter(&gpf->strokes, gpf->strokes.last, gps); + } + break; + /* Bring Forward */ + case GP_STROKE_MOVE_UP: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkafter(&gpf->strokes, gps->next, gps); + } + break; + /* Send Backward */ + case GP_STROKE_MOVE_DOWN: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkbefore(&gpf->strokes, gps->prev, gps); + } + break; + /* Send to Back */ + case GP_STROKE_MOVE_BOTTOM: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); + } + break; + default: + BLI_assert(0); + break; + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_arrange(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""}, + {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""}, + {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""}, + {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""}, + {0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Arrange Stroke"; + ot->idname = "GPENCIL_OT_stroke_arrange"; + ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; + + /* api callbacks */ + ot->exec = gp_stroke_arrange_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", ""); +} +/* ******************* Move Stroke to new color ************************** */ + +static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette; + bGPDpalettecolor *color; + + /* sanity checks */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + + palette = gpencil_palette_getactive(gpd); + color = gpencil_palettecolor_getactive(palette); + if (ELEM(NULL, palette, color)) { + return OPERATOR_CANCELLED; + } + + /* loop all strokes */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) + continue; + + /* asign new color (only if different) */ + if (STREQ(gps->colorname, color->info) == false) { + strcpy(gps->colorname, color->info); + gps->flag |= GP_STROKE_RECALC_COLOR; + } + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_change_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Stroke Color"; + ot->idname = "GPENCIL_OT_stroke_change_color"; + ot->description = "Move selected strokes to active color"; + + /* api callbacks */ + ot->exec = gp_stroke_change_color_exec; + ot->poll = gp_active_layer_poll; +} + +/* ******************* Lock color of non selected Strokes colors ************************** */ + +static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette; + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + palette = gpencil_palette_getactive(gpd); + if (ELEM(NULL, palette)) + return OPERATOR_CANCELLED; + + /* first lock all colors */ + for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag |= PC_COLOR_LOCKED; + } + + /* loop all selected strokes and unlock any color */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* unlock color */ + if (gps->palcolor != NULL) { + gps->palcolor->flag &= ~PC_COLOR_LOCKED; + } + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Lock Unused Colors"; + ot->idname = "GPENCIL_OT_stroke_lock_color"; + ot->description = "Lock any color not used in any selected stroke"; + + /* api callbacks */ + ot->exec = gp_stroke_lock_color_exec; + ot->poll = gp_active_layer_poll; +} + +/* ******************* Apply layer thickness change to Strokes ************************** */ + +static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = gpencil_layer_getactive(gpd); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->frames.first)) + return OPERATOR_CANCELLED; + + /* loop all strokes */ + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* Apply thickness */ + gps->thickness = gps->thickness + gpl->thickness; + } + } + /* clear value */ + gpl->thickness = 0.0f; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Apply Stroke Thickness"; + ot->idname = "GPENCIL_OT_stroke_apply_thickness"; + ot->description = "Apply the thickness change of the layer to its strokes"; + + /* api callbacks */ + ot->exec = gp_stroke_apply_thickness_exec; + ot->poll = gp_active_layer_poll; +} + +/* ******************* Close Strokes ************************** */ + +enum { + GP_STROKE_CYCLIC_CLOSE = 1, + GP_STROKE_CYCLIC_OPEN = 2, + GP_STROKE_CYCLIC_TOGGLE = 3 +}; + +static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + const int type = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* loop all selected strokes */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + bGPDpalettecolor *palcolor = gps->palcolor; + + /* skip strokes that are not selected or invalid for current view */ + if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* skip hidden or locked colors */ + if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED)) + continue; + + switch (type) { + case GP_STROKE_CYCLIC_CLOSE: + /* Close all (enable) */ + gps->flag |= GP_STROKE_CYCLIC; + break; + case GP_STROKE_CYCLIC_OPEN: + /* Open all (disable) */ + gps->flag &= ~GP_STROKE_CYCLIC; + break; + case GP_STROKE_CYCLIC_TOGGLE: + /* Just toggle flag... */ + gps->flag ^= GP_STROKE_CYCLIC; + break; + default: + BLI_assert(0); + break; + } + } + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +/** + * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with + * option to force opened/closed strokes instead of just toggle behavior. + */ +void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot) +{ + static EnumPropertyItem cyclic_type[] = { + {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""}, + {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""}, + {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Set Cyclical State"; + ot->idname = "GPENCIL_OT_stroke_cyclical_set"; + ot->description = "Close or open the selected stroke adding an edge from last to first point"; + + /* api callbacks */ + ot->exec = gp_stroke_cyclical_set_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", ""); +} + +/* ******************* Stroke join ************************** */ + +/* Helper: flip stroke */ +static void gpencil_flip_stroke(bGPDstroke *gps) +{ + bGPDspoint pt, *point, *point2; + int end = gps->totpoints - 1; + + for (int i = 0; i < gps->totpoints / 2; i++) { + /* save first point */ + point = &gps->points[i]; + pt.x = point->x; + pt.y = point->y; + pt.z = point->z; + pt.flag = point->flag; + pt.pressure = point->pressure; + pt.strength = point->strength; + pt.time = point->time; + + /* replace first point with last point */ + point2 = &gps->points[end]; + point->x = point2->x; + point->y = point2->y; + point->z = point2->z; + point->flag = point2->flag; + point->pressure = point2->pressure; + point->strength = point2->strength; + point->time = point2->time; + + /* replace last point with first saved before */ + point = &gps->points[end]; + point->x = pt.x; + point->y = pt.y; + point->z = pt.z; + point->flag = pt.flag; + point->pressure = pt.pressure; + point->strength = pt.strength; + point->time = pt.time; + + end--; + } +} + +/* Helper: copy point between strokes */ +static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3], + float pressure, float strength, float deltatime) +{ + bGPDspoint *newpoint; + + gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + gps->totpoints++; + + newpoint = &gps->points[gps->totpoints - 1]; + newpoint->x = point->x * delta[0]; + newpoint->y = point->y * delta[1]; + newpoint->z = point->z * delta[2]; + newpoint->flag = point->flag; + newpoint->pressure = pressure; + newpoint->strength = strength; + newpoint->time = point->time + deltatime; +} + +/* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */ +static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b) +{ + bGPDspoint point, *pt; + int i; + float delta[3] = {1.0f, 1.0f, 1.0f}; + float deltatime = 0.0f; + + /* sanity checks */ + if (ELEM(NULL, gps_a, gps_b)) + return; + + if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0)) + return; + + /* define start and end points of each stroke */ + float sa[3], sb[3], ea[3], eb[3]; + pt = &gps_a->points[0]; + copy_v3_v3(sa, &pt->x); + + pt = &gps_a->points[gps_a->totpoints - 1]; + copy_v3_v3(ea, &pt->x); + + pt = &gps_b->points[0]; + copy_v3_v3(sb, &pt->x); + + pt = &gps_b->points[gps_b->totpoints - 1]; + copy_v3_v3(eb, &pt->x); + /* review if need flip stroke B */ + float ea_sb = len_squared_v3v3(ea, sb); + float ea_eb = len_squared_v3v3(ea, eb); + /* flip if distance to end point is shorter */ + if (ea_eb < ea_sb) { + gpencil_flip_stroke(gps_b); + } + + /* 1st: add one tail point to start invisible area */ + point = gps_a->points[gps_a->totpoints - 1]; + deltatime = point.time; + gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f); + + /* 2nd: add one head point to finish invisible area */ + point = gps_b->points[0]; + gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime); + + /* 3rd: add all points */ + for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { + /* check if still room in buffer */ + if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) { + gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime); + } + } +} + +enum { + GP_STROKE_JOIN = -1, + GP_STROKE_JOINCOPY = 1 +}; + +static int gp_stroke_join_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *activegpl = gpencil_layer_getactive(gpd); + bGPDstroke *gps, *gpsn; + bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + + bGPDframe *gpf_a = NULL; + bGPDstroke *stroke_a = NULL; + bGPDstroke *stroke_b = NULL; + bGPDstroke *new_stroke = NULL; + + int type = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + if (activegpl->flag & GP_LAYER_LOCKED) + return OPERATOR_CANCELLED; + + BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY)); + + + /* read all selected strokes */ + bool first = false; + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *gpf = gpl->actframe; + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + /* to join strokes, cyclic must be disabled */ + gps->flag &= ~GP_STROKE_CYCLIC; + /* saves first frame and stroke */ + if (!first) { + first = true; + gpf_a = gpf; + stroke_a = gps; + } + else { + stroke_b = gps; + /* create a new stroke if was not created before (only created if something to join) */ + if (new_stroke == NULL) { + new_stroke = MEM_dupallocN(stroke_a); + new_stroke->points = MEM_dupallocN(stroke_a->points); + new_stroke->triangles = NULL; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + /* if new, set current color */ + if (type == GP_STROKE_JOINCOPY) { + new_stroke->palcolor = palcolor; + strcpy(new_stroke->colorname, palcolor->info); + new_stroke->flag |= GP_STROKE_RECALC_COLOR; + } + } + /* join new_stroke and stroke B. New stroke will contain all the previous data */ + gpencil_stroke_join_strokes(new_stroke, stroke_b); + + /* if join only, delete old strokes */ + if (type == GP_STROKE_JOIN) { + if (stroke_a) { + BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke); + BLI_remlink(&gpf->strokes, stroke_a); + free_gpencil_stroke(stroke_a); + stroke_a = NULL; + } + if (stroke_b) { + BLI_remlink(&gpf->strokes, stroke_b); + free_gpencil_stroke(stroke_b); + stroke_b = NULL; + } + } + } + } + } + } + CTX_DATA_END; + /* add new stroke if was not added before */ + if (type == GP_STROKE_JOINCOPY) { + if (new_stroke) { + /* Add a new frame if needed */ + if (activegpl->actframe == NULL) + activegpl->actframe = gpencil_frame_addnew(activegpl, gpf_a->framenum); + + BLI_addtail(&activegpl->actframe->strokes, new_stroke); + } + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_join(wmOperatorType *ot) +{ + static EnumPropertyItem join_type[] = { + {GP_STROKE_JOIN, "JOIN", 0, "Join", ""}, + {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Join Strokes"; + ot->idname = "GPENCIL_OT_stroke_join"; + ot->description = "Join selected strokes (optionally as new stroke)"; + + /* api callbacks */ + ot->exec = gp_stroke_join_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", ""); +} + +/* ******************* Stroke flip ************************** */ + +static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* read all selected strokes */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *gpf = gpl->actframe; + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + /* flip stroke */ + gpencil_flip_stroke(gps); + } + } + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_flip(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Flip Stroke"; + ot->idname = "GPENCIL_OT_stroke_flip"; + ot->description = "Change drawing direction of selected strokes"; + + /* api callbacks */ + ot->exec = gp_stroke_flip_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ************************************************ */ +/* Drawing Brushes Operators */ + +/* ******************* Add New Brush ************************ */ + +/* add new brush - wrapper around API */ +static int gp_brush_add_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); + return OPERATOR_CANCELLED; + } + /* add new brush now */ + gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Brush"; + ot->idname = "GPENCIL_OT_brush_add"; + ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil datablock"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_brush_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Remove Active Brush ************************* */ + +static int gp_brush_remove_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = gpencil_brush_getactive(ts); + + /* sanity checks */ + if (ELEM(NULL, ts, brush)) + return OPERATOR_CANCELLED; + + if (BLI_listbase_count(&ts->gp_brushes) < 2) { + BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush. Unable to delete brush"); + return OPERATOR_CANCELLED; + } + + + /* make the brush before this the new active brush + * - use the one after if this is the first + * - if this is the only brush, this naturally becomes NULL + */ + if (brush->prev) + gpencil_brush_setactive(ts, brush->prev); + else + gpencil_brush_setactive(ts, brush->next); + + /* delete the brush now... */ + gpencil_brush_delete(ts, brush); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove brush"; + ot->idname = "GPENCIL_OT_brush_remove"; + ot->description = "Remove active Grease Pencil drawing brush"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_brush_remove_exec; + ot->poll = gp_active_brush_poll; +} + +/* ********************** Change Brush ***************************** */ + +static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) +{ + uiPopupMenu *pup; + uiLayout *layout; + + /* call the menu, which will call this operator again, hence the canceled */ + pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + layout = UI_popup_menu_layout(pup); + uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush"); + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +static int gp_brush_change_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = NULL; + int brush_num = RNA_enum_get(op->ptr, "brush"); + + /* Get brush or create new one */ + if (brush_num == -1) { + /* Create brush */ + brush = gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); + } + else { + /* Try to get brush */ + brush = BLI_findlink(&ts->gp_brushes, brush_num); + + if (brush == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num); + return OPERATOR_CANCELLED; + } + } + + /* Set active brush */ + gpencil_brush_setactive(ts, brush); + + /* updates */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_change(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Brush"; + ot->idname = "GPENCIL_OT_brush_change"; + ot->description = "Change active Grease Pencil drawing brush"; + + /* callbacks */ + ot->invoke = gp_brush_change_invoke; + ot->exec = gp_brush_change_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* gp brush to use (dynamic enum) */ + ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", ""); + RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf); +} + +/* ******************* Move Brush Up/Down ************************** */ + +enum { + GP_BRUSH_MOVE_UP = -1, + GP_BRUSH_MOVE_DOWN = 1 +}; + +static int gp_brush_move_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = gpencil_brush_getactive(ts); + + int direction = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, ts, brush)) { + return OPERATOR_CANCELLED; + } + + /* up or down? */ + if (direction == GP_BRUSH_MOVE_UP) { + /* up */ + BLI_remlink(&ts->gp_brushes, brush); + BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush); + } + else if (direction == GP_BRUSH_MOVE_DOWN) { + /* down */ + BLI_remlink(&ts->gp_brushes, brush); + BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush); + } + else { + BLI_assert(0); + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_move(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""}, + {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Move Brush"; + ot->idname = "GPENCIL_OT_brush_move"; + ot->description = "Move the active Grease Pencil drawing brush up/down in the list"; + + /* api callbacks */ + ot->exec = gp_brush_move_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", ""); +} + +/* ******************* Brush create presets ************************** */ + +static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + gpencil_brush_init_presets(ts); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_presets_create(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Create Preset Brushes"; + ot->idname = "GPENCIL_OT_brush_presets_create"; + ot->description = "Create a set of predefined Grease Pencil drawing brushes"; + + /* api callbacks */ + ot->exec = gp_brush_presets_create_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + +} + +/* ***************** Copy Brush ************************ */ + +static int gp_brush_copy_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); + return OPERATOR_CANCELLED; + } + + bGPDbrush *brush = gpencil_brush_getactive(ts); + bGPDbrush *newbrush; + + /* sanity checks */ + if (ELEM(NULL, brush)) + return OPERATOR_CANCELLED; + + /* create a brush and duplicate data */ + newbrush = gpencil_brush_addnew(ts, brush->info, true); + newbrush->thickness = brush->thickness; + newbrush->draw_smoothfac = brush->draw_smoothfac; + newbrush->draw_smoothlvl = brush->draw_smoothlvl; + newbrush->sublevel = brush->sublevel; + newbrush->flag = brush->flag; + newbrush->draw_sensitivity = brush->draw_sensitivity; + newbrush->draw_strength = brush->draw_strength; + newbrush->draw_jitter = brush->draw_jitter; + newbrush->draw_angle = brush->draw_angle; + newbrush->draw_angle_factor = brush->draw_angle_factor; + newbrush->draw_random_press = brush->draw_random_press; + newbrush->draw_random_sub = brush->draw_random_sub; + + /* free automatic curves created by default (replaced by copy) */ + curvemapping_free(newbrush->cur_sensitivity); + curvemapping_free(newbrush->cur_strength); + curvemapping_free(newbrush->cur_jitter); + + /* make a copy of curves */ + newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity); + newbrush->cur_strength = curvemapping_copy(brush->cur_strength); + newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter); + + gpencil_brush_setactive(ts, newbrush); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Brush"; + ot->idname = "GPENCIL_OT_brush_copy"; + ot->description = "Copy current Grease Pencil drawing brush"; + + /* callbacks */ + ot->exec = gp_brush_copy_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ***************** Select Brush ************************ */ + +static int gp_brush_select_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere to go"); + return OPERATOR_CANCELLED; + } + + const int index = RNA_int_get(op->ptr, "index"); + bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index); + /* sanity checks */ + if (ELEM(NULL, brush)) { + return OPERATOR_CANCELLED; + } + + gpencil_brush_setactive(ts, brush); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Brush"; + ot->idname = "GPENCIL_OT_brush_select"; + ot->description = "Select a Grease Pencil drawing brush"; + + /* callbacks */ + ot->exec = gp_brush_select_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX); +} + +/* ************************************************ */ +/* Palette Operators */ + +/* ******************* Add New Palette ************************ */ + +/* add new palette - wrapper around API */ +static int gp_palette_add_exec(bContext *C, wmOperator *op) +{ + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + + /* if there's no existing Grease-Pencil data there, add some */ + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; + } + if (*gpd_ptr == NULL) + *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); + + /* add new palette now */ + gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Palette"; + ot->idname = "GPENCIL_OT_palette_add"; + ot->description = "Add new Grease Pencil palette for the active Grease Pencil datablock"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palette_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Remove Active Palette ************************* */ + +static int gp_palette_remove_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = gpencil_palette_getactive(gpd); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + if (BLI_listbase_count(&gpd->palettes) < 2) { + BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette. Unable to delete palette"); + return OPERATOR_CANCELLED; + } + + + /* make the palette before this the new active palette + * - use the one after if this is the first + * - if this is the only palette, this naturally becomes NULL + */ + if (palette->prev) + gpencil_palette_setactive(gpd, palette->prev); + else + gpencil_palette_setactive(gpd, palette->next); + + /* delete the palette now... */ + gpencil_palette_delete(gpd, palette); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove palette"; + ot->idname = "GPENCIL_OT_palette_remove"; + ot->description = "Remove active Grease Pencil palette"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palette_remove_exec; + ot->poll = gp_active_palette_poll; +} + +/* ********************** Change Palette ***************************** */ + +static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) +{ + uiPopupMenu *pup; + uiLayout *layout; + + /* call the menu, which will call this operator again, hence the canceled */ + pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + layout = UI_popup_menu_layout(pup); + uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette"); + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +static int gp_palette_change_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDpalette *palette = NULL; + int palette_num = RNA_enum_get(op->ptr, "palette"); + + /* Get palette or create new one */ + if (palette_num == -1) { + /* Create palette */ + palette = gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + } + else { + /* Try to get palette */ + palette = BLI_findlink(&gpd->palettes, palette_num); + + if (palette == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num); + return OPERATOR_CANCELLED; + } + } + + /* Set active palette */ + gpencil_palette_setactive(gpd, palette); + + /* updates */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_change(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Palette"; + ot->idname = "GPENCIL_OT_palette_change"; + ot->description = "Change active Grease Pencil palette"; + + /* callbacks */ + ot->invoke = gp_palette_change_invoke; + ot->exec = gp_palette_change_exec; + ot->poll = gp_active_palette_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* gp palette to use (dynamic enum) */ + ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", ""); + RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf); +} + +/* ******************* Lock and hide any color non used in current layer ************************** */ + +static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette; + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + palette = gpencil_palette_getactive(gpd); + if (ELEM(NULL, palette)) + return OPERATOR_CANCELLED; + + /* first lock and hide all colors */ + for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag |= PC_COLOR_LOCKED; + palcolor->flag |= PC_COLOR_HIDE; + } + + /* loop all selected strokes and unlock any color used in active layer */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + /* unlock/unhide color if not unlocked before */ + if (gps->palcolor != NULL) { + gps->palcolor->flag &= ~PC_COLOR_LOCKED; + gps->palcolor->flag &= ~PC_COLOR_HIDE; + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Disable Unused Layer Colors"; + ot->idname = "GPENCIL_OT_palette_lock_layer"; + ot->description = "Lock and hide any color not used in any layer"; + + /* api callbacks */ + ot->exec = gp_palette_lock_layer_exec; + ot->poll = gp_active_layer_poll; +} + +/* ************************************************ */ +/* Palette Colors Operators */ + +/* ******************* Add New Palette ************************ */ + +/* add new palette - wrapper around API */ +static int gp_palettecolor_add_exec(bContext *C, wmOperator *op) +{ + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + + /* if there's no existing Grease-Pencil data there, add some */ + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; + } + if (*gpd_ptr == NULL) + *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); + + /* verify palette */ + bGPDpalette *palette = gpencil_palette_getactive(*gpd_ptr); + if (palette == NULL) + palette = gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); + + /* add new palette color now */ + gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Palette Color"; + ot->idname = "GPENCIL_OT_palettecolor_add"; + ot->description = "Add new Grease Pencil palette color for the active Grease Pencil datablock"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palettecolor_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Remove Active Palette color ************************* */ + +static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalettecolor *color = gpencil_palettecolor_getactive(palette); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, color)) + return OPERATOR_CANCELLED; + + /* make the palette color before this the new active color + * - use the one after if this is the first + * - if this is the only color, this naturally becomes NULL + */ + if (color->prev) + gpencil_palettecolor_setactive(palette, color->prev); + else + gpencil_palettecolor_setactive(palette, color->next); + + /* delete the strokes */ + gpencil_palettecolor_delete_strokes(gpd, color->info); + + /* delete the palette color now... */ + gpencil_palettecolor_delete(palette, color); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove palette color"; + ot->idname = "GPENCIL_OT_palettecolor_remove"; + ot->description = "Remove active Grease Pencil palette color"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palettecolor_remove_exec; + ot->poll = gp_active_palettecolor_poll; +} + +/* ********************** Isolate palette color **************************** */ + +static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalettecolor *active_color = gpencil_palettecolor_getactive(palette); + bGPDpalettecolor *palcolor; + + int flags = PC_COLOR_LOCKED; + bool isolate = false; + + if (RNA_boolean_get(op->ptr, "affect_visibility")) + flags |= PC_COLOR_HIDE; + + if (ELEM(NULL, gpd, active_color)) { + BKE_report(op->reports, RPT_ERROR, "No active color to isolate"); + return OPERATOR_CANCELLED; + } + + /* Test whether to isolate or clear all flags */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + /* Skip if this is the active one */ + if (palcolor == active_color) + continue; + + /* If the flags aren't set, that means that the color is + * not alone, so we have some colors to isolate still + */ + if ((palcolor->flag & flags) == 0) { + isolate = true; + break; + } + } + + /* Set/Clear flags as appropriate */ + if (isolate) { + /* Set flags on all "other" colors */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + if (palcolor == active_color) + continue; + else + palcolor->flag |= flags; + } + } + else { + /* Clear flags - Restore everything else */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~flags; + } + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Isolate Palette Color"; + ot->idname = "GPENCIL_OT_palettecolor_isolate"; + ot->description = "Toggle whether the active color is the only one that is editable and/or visible"; + + /* callbacks */ + ot->exec = gp_isolate_palettecolor_exec; + ot->poll = gp_active_palettecolor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling " + "the editability, also affect the visibility"); +} + +/* *********************** Hide Palette colors ******************************** */ + +static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + + bool unselected = RNA_boolean_get(op->ptr, "unselected"); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + if (unselected) { + bGPDpalettecolor *color; + + /* hide unselected */ + for (color = palette->colors.first; color; color = color->next) { + if (color != palcolor) { + color->flag |= PC_COLOR_HIDE; + } + } + } + else { + /* hide selected/active */ + palcolor->flag |= PC_COLOR_HIDE; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Color(s)"; + ot->idname = "GPENCIL_OT_palettecolor_hide"; + ot->description = "Hide selected/unselected Grease Pencil colors"; + + /* callbacks */ + ot->exec = gp_palettecolor_hide_exec; + ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors"); +} + +/* ********************** Show All Colors ***************************** */ + +/* poll callback for showing colors */ +static int gp_palettecolor_reveal_poll(bContext *C) +{ + return ED_gpencil_data_get_active(C) != NULL; +} + +static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + /* make all colors visible */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~PC_COLOR_HIDE; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Show All Colors"; + ot->idname = "GPENCIL_OT_palettecolor_reveal"; + ot->description = "Unhide all hidden Grease Pencil palette colors"; + + /* callbacks */ + ot->exec = gp_palettecolor_reveal_exec; + ot->poll = gp_palettecolor_reveal_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ***************** Lock/Unlock All Palette colors ************************ */ + +static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + /* make all layers non-editable */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag |= PC_COLOR_LOCKED; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Lock All Colors"; + ot->idname = "GPENCIL_OT_palettecolor_lock_all"; + ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified"; + + /* callbacks */ + ot->exec = gp_palettecolor_lock_all_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* -------------------------- */ + +static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + /* make all layers editable again*/ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~PC_COLOR_LOCKED; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Unlock All Colors"; + ot->idname = "GPENCIL_OT_palettecolor_unlock_all"; + ot->description = "Unlock all Grease Pencil colors so that they can be edited"; + + /* callbacks */ + ot->exec = gp_palettecolor_unlock_all_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************* Move Color Up/Down ************************** */ + +enum { + GP_COLOR_MOVE_UP = -1, + GP_COLOR_MOVE_DOWN = 1 +}; + +static int gp_palettecolor_move_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + + int direction = RNA_enum_get(op->ptr, "direction"); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + /* up or down? */ + if (direction == GP_COLOR_MOVE_UP) { + /* up */ + BLI_remlink(&palette->colors, palcolor); + BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor); + } + else if (direction == GP_COLOR_MOVE_DOWN) { + /* down */ + BLI_remlink(&palette->colors, palcolor); + BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor); + } + else { + BLI_assert(0); + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_move(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""}, + {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Move Palette color"; + ot->idname = "GPENCIL_OT_palettecolor_move"; + ot->description = "Move the active Grease Pencil palette color up/down in the list"; + + /* api callbacks */ + ot->exec = gp_palettecolor_move_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", ""); +} + +/* ***************** Select all strokes using Palette color ************************ */ + +static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + /* read all strokes and select*/ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + /* verify something to do */ + for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) + continue; + + /* select */ + if (strcmp(palcolor->info, gps->colorname) == 0) { + bGPDspoint *pt; + int i; + + gps->flag |= GP_STROKE_SELECT; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag |= GP_SPOINT_SELECT; + } + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Color"; + ot->idname = "GPENCIL_OT_palettecolor_select"; + ot->description = "Select all Grease Pencil strokes using current color"; + + /* callbacks */ + ot->exec = gp_palettecolor_select_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ***************** Copy Palette color ************************ */ + +static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + bGPDpalettecolor *newcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + /* create a new color and duplicate data */ + newcolor = gpencil_palettecolor_addnew(palette, palcolor->info, true); + copy_v4_v4(newcolor->color, palcolor->color); + copy_v4_v4(newcolor->fill, palcolor->fill); + newcolor->flag = palcolor->flag; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Color"; + ot->idname = "GPENCIL_OT_palettecolor_copy"; + ot->description = "Copy current Grease Pencil palette color"; + + /* callbacks */ + ot->exec = gp_palettecolor_copy_exec; + ot->poll = gp_active_palettecolor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index ac49a51c716..621ebea6603 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -125,10 +125,48 @@ static int gp_stroke_edit_poll(bContext *C) return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; } +/* ************ Stroke Hide selection Toggle ************** */ + +static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + if (ts == NULL) + return OPERATOR_CANCELLED; + + /* Just toggle alpha... */ + if (ts->gp_sculpt.alpha > 0.0f) { + ts->gp_sculpt.alpha = 0.0f; + } + else { + ts->gp_sculpt.alpha = 1.0f; + } + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Selection"; + ot->idname = "GPENCIL_OT_selection_opacity_toggle"; + ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor"; + + /* callbacks */ + ot->exec = gpencil_hideselect_toggle_exec; + ot->poll = gp_stroke_edit_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; +} + /* ************** Duplicate Selected Strokes **************** */ /* Make copies of selected point segments in a selected stroke */ -static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes) +static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername) { bGPDspoint *pt; int i; @@ -169,6 +207,7 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes) /* make a stupid copy first of the entire stroke (to get the flags too) */ gpsd = MEM_dupallocN(gps); + strcpy(gpsd->tmp_layerinfo, layername); /* saves original layer name */ /* initialize triangle memory - will be calculated on next redraw */ gpsd->triangles = NULL; @@ -216,8 +255,9 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) /* make copies of selected strokes, and deselect these once we're done */ for (gps = gpf->strokes.first; gps; gps = gps->next) { /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) + if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; + } if (gps->flag & GP_STROKE_SELECT) { if (gps->totpoints == 1) { @@ -226,8 +266,9 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); + strcpy(gpsd->tmp_layerinfo, gpl->info); gpsd->points = MEM_dupallocN(gps->points); - + /* triangle information - will be calculated on next redraw */ gpsd->flag |= GP_STROKE_RECALC_CACHES; gpsd->triangles = NULL; @@ -238,7 +279,7 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) } else { /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ - gp_duplicate_points(gps, &new_strokes); + gp_duplicate_points(gps, &new_strokes, gpl->info); } /* deselect original stroke, or else the originals get moved too @@ -345,6 +386,7 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); + strcpy(gpsd->tmp_layerinfo, gpl->info); /* saves original layer name */ gpsd->points = MEM_dupallocN(gps->points); /* triangles cache - will be recalculated on next redraw */ @@ -358,7 +400,7 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) } else { /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ - gp_duplicate_points(gps, &gp_strokes_copypastebuf); + gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info); } } } @@ -395,13 +437,20 @@ static int gp_strokes_paste_poll(bContext *C) return (CTX_data_active_gpencil_layer(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf)); } +enum { + GP_COPY_ONLY = -1, + GP_COPY_MERGE = 1 +}; + static int gp_strokes_paste_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */ bGPDframe *gpf; - + + int type = RNA_enum_get(op->ptr, "type"); + /* check for various error conditions */ if (gpd == NULL) { BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); @@ -415,7 +464,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) /* no active layer - let's just create one */ gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); } - else if (gpencil_layer_is_editable(gpl) == false) { + else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) { BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked"); return OPERATOR_CANCELLED; } @@ -463,26 +512,34 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) * we are obliged to add a new frame if one * doesn't exist already */ - gpf = gpencil_layer_getframe(gpl, CFRA, true); - if (gpf) { bGPDstroke *gps; - /* Copy each stroke into the layer */ for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use(C, gps)) { - bGPDstroke *new_stroke = MEM_dupallocN(gps); - - new_stroke->points = MEM_dupallocN(gps->points); - - new_stroke->flag |= GP_STROKE_RECALC_CACHES; - new_stroke->triangles = NULL; - - new_stroke->next = new_stroke->prev = NULL; - BLI_addtail(&gpf->strokes, new_stroke); + /* need to verify if layer exist nad frame */ + if (type != GP_COPY_MERGE) { + gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info)); + if (gpl == NULL) { + /* no layer - use active (only if layer deleted before paste) */ + gpl = CTX_data_active_gpencil_layer(C); + } + } + gpf = gpencil_layer_getframe(gpl, CFRA, true); + if (gpf) { + bGPDstroke *new_stroke = MEM_dupallocN(gps); + new_stroke->tmp_layerinfo[0] = '\0'; + + new_stroke->points = MEM_dupallocN(gps->points); + + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + new_stroke->triangles = NULL; + + new_stroke->next = new_stroke->prev = NULL; + BLI_addtail(&gpf->strokes, new_stroke); + } } } - } /* updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -492,10 +549,16 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) void GPENCIL_OT_paste(wmOperatorType *ot) { + static EnumPropertyItem copy_type[] = { + {GP_COPY_ONLY, "COPY", 0, "Copy", ""}, + {GP_COPY_MERGE, "MERGE", 0, "Merge", ""}, + {0, NULL, 0, NULL, NULL} + }; + /* identifiers */ ot->name = "Paste Strokes"; ot->idname = "GPENCIL_OT_paste"; - ot->description = "Paste previously copied strokes into active layer"; + ot->description = "Paste previously copied strokes or copy and merge in active layer"; /* callbacks */ ot->exec = gp_strokes_paste_exec; @@ -503,6 +566,8 @@ void GPENCIL_OT_paste(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", ""); } /* ******************* Move To Layer ****************************** */ @@ -1069,7 +1134,7 @@ void GPENCIL_OT_delete(wmOperatorType *ot) }; /* identifiers */ - ot->name = "Delete..."; + ot->name = "Delete"; ot->idname = "GPENCIL_OT_delete"; ot->description = "Delete selected Grease Pencil strokes, vertices, or frames"; @@ -1124,25 +1189,65 @@ static int gp_snap_poll(bContext *C) static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) { RegionView3D *rv3d = CTX_wm_region_data(C); - float gridf = rv3d->gridview; + const float gridf = rv3d->gridview; - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - bGPDspoint *pt; - int i; - - // TOOD: if entire stroke is selected, offset entire stroke by same amount? - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - /* only if point is selected.. */ - if (pt->flag & GP_SPOINT_SELECT) { - pt->x = gridf * floorf(0.5f + pt->x / gridf); - pt->y = gridf * floorf(0.5f + pt->y / gridf); - pt->z = gridf * floorf(0.5f + pt->z / gridf); + bGPdata *gpd = ED_gpencil_data_get_active(C); + float diff_mat[4][4]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + + bGPDspoint *pt; + int i; + + // TOOD: if entire stroke is selected, offset entire stroke by same amount? + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + + /* only if point is selected.. */ + if (pt->flag & GP_SPOINT_SELECT) { + if (gpl->parent == NULL) { + pt->x = gridf * floorf(0.5f + pt->x / gridf); + pt->y = gridf * floorf(0.5f + pt->y / gridf); + pt->z = gridf * floorf(0.5f + pt->z / gridf); + } + else { + /* apply parent transformations */ + float fpt[3]; + mul_v3_m4v3(fpt, diff_mat, &pt->x); + + fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); + fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); + fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); + + /* return data */ + copy_v3_v3(&pt->x, fpt); + gp_apply_parent_point(gpl, pt); + } + + } + } + } } } - CTX_DATA_END; WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1169,41 +1274,68 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); - + const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d); - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - bGPDspoint *pt; - int i; - - /* only continue if this stroke is selected (editable doesn't guarantee this)... */ - if ((gps->flag & GP_STROKE_SELECT) == 0) - continue; - - if (use_offset) { - float offset[3]; - - /* compute offset from first point of stroke to cursor */ - /* TODO: Allow using midpoint instead? */ - sub_v3_v3v3(offset, cursor_global, &gps->points->x); - - /* apply offset to all points in the stroke */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - add_v3_v3(&pt->x, offset); + + bGPdata *gpd = ED_gpencil_data_get_active(C); + float diff_mat[4][4]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); } - } - else { - /* affect each selected point */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - copy_v3_v3(&pt->x, cursor_global); + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + + bGPDspoint *pt; + int i; + + /* only continue if this stroke is selected (editable doesn't guarantee this)... */ + if ((gps->flag & GP_STROKE_SELECT) == 0) + continue; + + if (use_offset) { + float offset[3]; + + /* compute offset from first point of stroke to cursor */ + /* TODO: Allow using midpoint instead? */ + sub_v3_v3v3(offset, cursor_global, &gps->points->x); + + /* apply offset to all points in the stroke */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + add_v3_v3(&pt->x, offset); + } + } + else { + /* affect each selected point */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + copy_v3_v3(&pt->x, cursor_global); + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } + } + } } + + } } } - CTX_DATA_END; WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1243,24 +1375,57 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) INIT_MINMAX(min, max); /* calculate midpoints from selected points */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - bGPDspoint *pt; - int i; - - /* only continue if this stroke is selected (editable doesn't guarantee this)... */ - if ((gps->flag & GP_STROKE_SELECT) == 0) - continue; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - add_v3_v3(centroid, &pt->x); - minmax_v3v3_v3(min, max, &pt->x); - count++; + bGPdata *gpd = ED_gpencil_data_get_active(C); + float diff_mat[4][4]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + + bGPDspoint *pt; + int i; + + /* only continue if this stroke is selected (editable doesn't guarantee this)... */ + if ((gps->flag & GP_STROKE_SELECT) == 0) + continue; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + if (gpl->parent == NULL) { + add_v3_v3(centroid, &pt->x); + minmax_v3v3_v3(min, max, &pt->x); + } + else { + /* apply parent transformations */ + float fpt[3]; + mul_v3_m4v3(fpt, diff_mat, &pt->x); + + add_v3_v3(centroid, fpt); + minmax_v3v3_v3(min, max, fpt); + } + count++; + } + } + } } } - CTX_DATA_END; if (v3d->around == V3D_AROUND_CENTER_MEAN && count) { mul_v3_fl(centroid, 1.0f / (float)count); diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 53fb33eeb9b..0ff0878d4ce 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -101,6 +101,23 @@ void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct int *r_x, int *r_y); /** + * Convert point to parent space + * + * \param pt Original point + * \param diff_mat Matrix with the difference between original parent matrix + * \param[out] r_pt Pointer to new point after apply matrix + */ +void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt); +/** + * Change points position relative to parent object + */ +void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps); +/** + * Change point position relative to parent object + */ +void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt); + +/** * Convert a screenspace point to a 3D Grease Pencil coordinate. * * For use with editing tools where it is easier to perform the operations in 2D, @@ -116,6 +133,10 @@ bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, struct Scene *scene, const float int gp_add_poll(struct bContext *C); int gp_active_layer_poll(struct bContext *C); +int gp_active_brush_poll(struct bContext *C); +int gp_active_palette_poll(struct bContext *C); +int gp_active_palettecolor_poll(struct bContext *C); +int gp_brush_crt_presets_poll(bContext *C); /* Copy/Paste Buffer --------------------------------- */ /* gpencil_edit.c */ @@ -137,17 +158,47 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure); /** +* Apply smooth for strength to stroke point +* \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); + +/** +* Apply smooth for thickness to stroke point (use pressure) +* \param gps Stroke to smooth +* \param i Point index +* \param inf Amount of smoothing to apply +*/ +bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf); + +/** * Subdivide a stroke once, by adding points at the midpoint between each pair of points * \param gps Stroke data * \param new_totpoints Total number of points (after subdividing) */ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints); +/** +* Add randomness to stroke +* \param gps Stroke data +* \param brsuh Brush data +*/ +void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush); + /* Layers Enums -------------------------------------- */ struct EnumPropertyItem *ED_gpencil_layers_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); +/* Enums of GP Brushes */ +EnumPropertyItem *ED_gpencil_brushes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free); + +/* Enums of GP palettes */ +EnumPropertyItem *ED_gpencil_palettes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free); /* ***************************************************** */ /* Operator Defines */ @@ -166,6 +217,7 @@ typedef enum eGPencil_PaintModes { /* stroke editing ----- */ void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot); +void GPENCIL_OT_selection_opacity_toggle(struct wmOperatorType *ot); void GPENCIL_OT_select(struct wmOperatorType *ot); void GPENCIL_OT_select_all(struct wmOperatorType *ot); @@ -216,12 +268,45 @@ void GPENCIL_OT_lock_all(struct wmOperatorType *ot); void GPENCIL_OT_unlock_all(struct wmOperatorType *ot); void GPENCIL_OT_layer_isolate(struct wmOperatorType *ot); +void GPENCIL_OT_layer_merge(struct wmOperatorType *ot); void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot); void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_arrange(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_change_color(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_lock_color(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_apply_thickness(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_cyclical_set(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_join(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_flip(struct wmOperatorType *ot); + +void GPENCIL_OT_brush_add(struct wmOperatorType *ot); +void GPENCIL_OT_brush_remove(struct wmOperatorType *ot); +void GPENCIL_OT_brush_change(struct wmOperatorType *ot); +void GPENCIL_OT_brush_move(struct wmOperatorType *ot); +void GPENCIL_OT_brush_presets_create(struct wmOperatorType *ot); +void GPENCIL_OT_brush_copy(struct wmOperatorType *ot); +void GPENCIL_OT_brush_select(struct wmOperatorType *ot); + +void GPENCIL_OT_palette_add(struct wmOperatorType *ot); +void GPENCIL_OT_palette_remove(struct wmOperatorType *ot); +void GPENCIL_OT_palette_change(struct wmOperatorType *ot); +void GPENCIL_OT_palette_lock_layer(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_add(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_remove(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_isolate(struct wmOperatorType *ot); + +void GPENCIL_OT_palettecolor_hide(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_reveal(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_lock_all(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_unlock_all(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_move(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_select(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_copy(struct wmOperatorType *ot); + /* undo stack ---------- */ void gpencil_undo_init(struct bGPdata *gpd); @@ -273,4 +358,39 @@ typedef enum ACTCONT_TYPES { ACTCONT_GPENCIL } ACTCONT_TYPES; +/** +* Iterate over all editable strokes in the current context, +* stopping on each usable layer + stroke pair (i.e. gpl and gps) +* to perform some operations on the stroke. +* +* \param gpl The identifier to use for the layer of the stroke being processed. +* Choose a suitable value to avoid name clashes. +* \param gps The identifier to use for current stroke being processed. +* Choose a suitable value to avoid name clashes. +*/ +#define GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) \ +{ \ + CTX_DATA_BEGIN(C, bGPDlayer*, gpl, editable_gpencil_layers) \ + { \ + if (gpl->actframe == NULL) \ + continue; \ + /* calculate difference matrix if parent object */ \ + float diff_mat[4][4]; \ + ED_gpencil_parent_location(gpl, diff_mat); \ + /* loop over strokes */ \ + for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { \ + /* skip strokes that are invalid for current view */ \ + if (ED_gpencil_stroke_can_use(C, gps) == false) \ + continue; \ + /* check if the color is editable */ \ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) \ + continue; \ + /* ... Do Stuff With Strokes ... */ + +#define GP_EDITABLE_STROKES_END \ + } \ + } \ + CTX_DATA_END; \ +} (void)0 + #endif /* __GPENCIL_INTERN_H__ */ diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 65ee1122b56..6bbb8f7c965 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -140,8 +140,8 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) * that the only data being edited is that of the Grease Pencil strokes */ - /* FKEY = Eraser Radius */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); + /* CTRL + FKEY = Eraser Radius */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); RNA_string_set(kmi->ptr, "data_path_primary", "user_preferences.edit.grease_pencil_eraser_radius"); @@ -169,8 +169,8 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0); RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength"); - /* Ctrl-FKEY = Sculpt Brush Size */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); + /* FKEY = Sculpt Brush Size */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size"); @@ -266,14 +266,37 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_hide", HKEY, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "unselected", true); + + WM_keymap_add_item(keymap, "GPENCIL_OT_selection_opacity_toggle", HKEY, KM_PRESS, KM_CTRL, 0); /* Isolate Layer */ WM_keymap_add_item(keymap, "GPENCIL_OT_layer_isolate", PADASTERKEY, KM_PRESS, 0, 0); /* Move to Layer */ WM_keymap_add_item(keymap, "GPENCIL_OT_move_to_layer", MKEY, KM_PRESS, 0, 0); - - + + /* Select drawing brush using index */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", ONEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 0); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", TWOKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 1); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", THREEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 2); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", FOURKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 3); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", FIVEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 4); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", SIXKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 5); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", SEVENKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 6); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", EIGHTKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 7); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", NINEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 8); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", ZEROKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 9); + /* Transform Tools */ kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0); @@ -318,7 +341,8 @@ void ED_operatortypes_gpencil(void) /* Editing (Strokes) ------------ */ WM_operatortype_append(GPENCIL_OT_editmode_toggle); - + WM_operatortype_append(GPENCIL_OT_selection_opacity_toggle); + WM_operatortype_append(GPENCIL_OT_select); WM_operatortype_append(GPENCIL_OT_select_all); WM_operatortype_append(GPENCIL_OT_select_circle); @@ -362,12 +386,44 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_lock_all); WM_operatortype_append(GPENCIL_OT_unlock_all); WM_operatortype_append(GPENCIL_OT_layer_isolate); - + WM_operatortype_append(GPENCIL_OT_layer_merge); + WM_operatortype_append(GPENCIL_OT_active_frame_delete); WM_operatortype_append(GPENCIL_OT_active_frames_delete_all); WM_operatortype_append(GPENCIL_OT_convert); + WM_operatortype_append(GPENCIL_OT_stroke_arrange); + WM_operatortype_append(GPENCIL_OT_stroke_change_color); + WM_operatortype_append(GPENCIL_OT_stroke_lock_color); + WM_operatortype_append(GPENCIL_OT_stroke_apply_thickness); + WM_operatortype_append(GPENCIL_OT_stroke_cyclical_set); + WM_operatortype_append(GPENCIL_OT_stroke_join); + WM_operatortype_append(GPENCIL_OT_stroke_flip); + + WM_operatortype_append(GPENCIL_OT_palette_add); + WM_operatortype_append(GPENCIL_OT_palette_remove); + WM_operatortype_append(GPENCIL_OT_palette_change); + WM_operatortype_append(GPENCIL_OT_palette_lock_layer); + WM_operatortype_append(GPENCIL_OT_palettecolor_add); + WM_operatortype_append(GPENCIL_OT_palettecolor_remove); + WM_operatortype_append(GPENCIL_OT_palettecolor_isolate); + WM_operatortype_append(GPENCIL_OT_palettecolor_hide); + WM_operatortype_append(GPENCIL_OT_palettecolor_reveal); + WM_operatortype_append(GPENCIL_OT_palettecolor_lock_all); + WM_operatortype_append(GPENCIL_OT_palettecolor_unlock_all); + WM_operatortype_append(GPENCIL_OT_palettecolor_move); + WM_operatortype_append(GPENCIL_OT_palettecolor_select); + WM_operatortype_append(GPENCIL_OT_palettecolor_copy); + + WM_operatortype_append(GPENCIL_OT_brush_add); + WM_operatortype_append(GPENCIL_OT_brush_remove); + WM_operatortype_append(GPENCIL_OT_brush_change); + WM_operatortype_append(GPENCIL_OT_brush_move); + WM_operatortype_append(GPENCIL_OT_brush_presets_create); + WM_operatortype_append(GPENCIL_OT_brush_copy); + WM_operatortype_append(GPENCIL_OT_brush_select); + /* Editing (Time) --------------- */ } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index a570d586f50..403d632b5da 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -39,21 +39,26 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_rand.h" #include "BLT_translation.h" #include "PIL_time.h" +#include "BKE_main.h" +#include "BKE_paint.h" #include "BKE_gpencil.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_report.h" #include "BKE_screen.h" #include "BKE_tracking.h" +#include "BKE_colortools.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "DNA_windowmanager_types.h" #include "UI_view2d.h" @@ -151,6 +156,10 @@ typedef struct tGPsdata { float custom_color[4]; /* custom color - hack for enforcing a particular color for track/mask editing */ void *erasercursor; /* radial cursor data for drawing eraser */ + + bGPDpalettecolor *palettecolor; /* current palette color */ + bGPDbrush *brush; /* current drawing brush */ + short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ } tGPsdata; /* ------ */ @@ -333,10 +342,90 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3] } } +/* apply jitter to stroke */ +static void gp_brush_jitter(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2], int r_mval[2]) +{ + float pressure = pt->pressure; + float tmp_pressure = pt->pressure; + if (brush->draw_jitter > 0.0f) { + float curvef = curvemapping_evaluateF(brush->cur_jitter, 0, pressure); + tmp_pressure = curvef * brush->draw_sensitivity; + } + const float exfactor = (brush->draw_jitter + 2.0f) * (brush->draw_jitter + 2.0f); /* exponential value */ + const float fac = BLI_frand() * exfactor * tmp_pressure; + /* Jitter is applied perpendicular to the mouse movement vector (2D space) */ + float mvec[2], svec[2]; + /* mouse movement in ints -> floats */ + if (gpd->sbuffer_size > 1) { + mvec[0] = (float)(mval[0] - (pt - 1)->x); + mvec[1] = (float)(mval[1] - (pt - 1)->y); + normalize_v2(mvec); + } + else { + mvec[0] = 0.0f; + mvec[1] = 0.0f; + } + /* rotate mvec by 90 degrees... */ + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + /* scale the displacement by the random, and apply */ + if (BLI_frand() > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + + r_mval[0] = mval[0] + svec[0]; + r_mval[1] = mval[1] + svec[1]; + +} + +/* apply pressure change depending of the angle of the stroke to simulate a pen with shape */ +static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2]) +{ + float mvec[2]; + float sen = brush->draw_angle_factor; /* sensitivity */; + float fac; + float mpressure; + + float angle = brush->draw_angle; /* default angle of brush in radians */; + float v0[2] = { cos(angle), sin(angle) }; /* angle vector of the brush with full thickness */ + + /* Apply to first point (only if there are 2 points because before no data to do it ) */ + if (gpd->sbuffer_size == 1) { + mvec[0] = (float)(mval[0] - (pt - 1)->x); + mvec[1] = (float)(mval[1] - (pt - 1)->y); + normalize_v2(mvec); + + /* uses > 1.0f to get a smooth transition in first point */ + fac = 1.4f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ + (pt - 1)->pressure = (pt - 1)->pressure - (sen * fac); + + CLAMP((pt - 1)->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); + } + + /* apply from second point */ + if (gpd->sbuffer_size >= 1) { + mvec[0] = (float)(mval[0] - (pt - 1)->x); + mvec[1] = (float)(mval[1] - (pt - 1)->y); + normalize_v2(mvec); + + fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ + /* interpolate with previous point for smoother transitions */ + mpressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f); + pt->pressure = mpressure; + + CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); + } + +} + /* add current stroke-point to buffer (returns whether point was successfully added) */ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, double curtime) { bGPdata *gpd = p->gpd; + bGPDbrush *brush = p->brush; tGPspoint *pt; /* check painting mode */ @@ -349,6 +438,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, /* store settings */ copy_v2_v2_int(&pt->x, mval); pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; pt->time = (float)(curtime - p->inittime); /* increment buffer size */ @@ -363,6 +453,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, /* store settings */ copy_v2_v2_int(&pt->x, mval); pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; pt->time = (float)(curtime - p->inittime); /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ @@ -381,8 +472,65 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, pt = ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size); /* store settings */ - copy_v2_v2_int(&pt->x, mval); - pt->pressure = pressure; + /* pressure */ + if (brush->flag & GP_BRUSH_USE_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); + pt->pressure = curvef * brush->draw_sensitivity; + } + else { + pt->pressure = 1.0f; + } + /* Apply jitter to position */ + if (brush->draw_jitter > 0.0f) { + int r_mval[2]; + gp_brush_jitter(gpd, brush, pt, mval, r_mval); + copy_v2_v2_int(&pt->x, r_mval); + } + else { + copy_v2_v2_int(&pt->x, mval); + } + /* apply randomness to pressure */ + if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_PRESSURE)) { + float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); + float tmp_pressure = curvef * brush->draw_sensitivity; + if (BLI_frand() > 0.5f) { + pt->pressure -= tmp_pressure * brush->draw_random_press * BLI_frand(); + } + else { + pt->pressure += tmp_pressure * brush->draw_random_press * BLI_frand(); + } + CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f); + } + + /* apply angle of stroke to brush size */ + if (brush->draw_angle_factor > 0.0f) { + gp_brush_angle(gpd, brush, pt, mval); + } + + /* color strength */ + if (brush->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->cur_strength, 0, pressure); + float tmp_pressure = curvef * brush->draw_sensitivity; + + pt->strength = tmp_pressure * brush->draw_strength; + } + else { + pt->strength = brush->draw_strength; + } + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + + /* apply randomness to color strength */ + if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_STRENGTH)) { + if (BLI_frand() > 0.5f) { + pt->strength -= pt->strength * brush->draw_random_press * BLI_frand(); + } + else { + pt->strength += pt->strength * brush->draw_random_press * BLI_frand(); + } + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + } + + /* point time */ pt->time = (float)(curtime - p->inittime); /* increment counters */ @@ -395,12 +543,15 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, return GP_STROKEADD_NORMAL; } else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + + bGPDlayer *gpl = gpencil_layer_getactive(gpd); /* get pointer to destination point */ pt = (tGPspoint *)(gpd->sbuffer); /* store settings */ copy_v2_v2_int(&pt->x, mval); pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; pt->time = (float)(curtime - p->inittime); /* if there's stroke for this poly line session add (or replace last) point @@ -433,10 +584,16 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pts); + } /* copy pressure and time */ pts->pressure = pt->pressure; + pts->strength = pt->strength; pts->time = pt->time; + /* force fill recalc */ + gps->flag |= GP_STROKE_RECALC_CACHES; } /* increment counters */ @@ -534,6 +691,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) bGPDstroke *gps; bGPDspoint *pt; tGPspoint *ptc; + bGPDbrush *brush = p->brush; int i, totelem; /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ @@ -569,7 +727,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* copy appropriate settings for stroke */ gps->totpoints = totelem; - gps->thickness = p->gpl->thickness; + gps->thickness = brush->thickness; gps->flag = gpd->sbuffer_sflag; gps->inittime = p->inittime; @@ -577,7 +735,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gps->flag |= GP_STROKE_RECALC_CACHES; /* allocate enough memory for a continuous array for storage points */ - int sublevel = gpl->sublevel; + int sublevel = brush->sublevel; int new_totpoints = gps->totpoints; for (i = 0; i < sublevel; i++) { @@ -600,9 +758,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; pt++; @@ -614,9 +777,15 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } + /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; } } @@ -626,9 +795,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; } else { @@ -703,6 +877,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; } @@ -716,25 +892,38 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gp_subdivide_stroke(gps, totpoints); } } - + /* apply randomness to stroke */ + if (brush->draw_random_sub > 0.0f) { + gp_randomize_stroke(gps, brush); + } + /* smooth stroke after subdiv - only if there's something to do * for each iteration, the factor is reduced to get a better smoothing without changing too much * the original stroke */ - if (gpl->draw_smoothfac > 0.0f) { + if (brush->draw_smoothfac > 0.0f) { float reduce = 0.0f; - for (int r = 0; r < gpl->draw_smoothlvl; ++r) { + for (int r = 0; r < brush->draw_smoothlvl; ++r) { for (i = 0; i < gps->totpoints; i++) { /* NOTE: No pressure smoothing, or else we get annoying thickness changes while drawing... */ - gp_smooth_stroke(gps, i, gpl->draw_smoothfac - reduce, false); + gp_smooth_stroke(gps, i, brush->draw_smoothfac - reduce, false); } reduce += 0.25f; // reduce the factor } } - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent(gpl, gps); + } + if (depth_arr) MEM_freeN(depth_arr); } + /* Save palette color */ + bGPDpalette *palette = gpencil_palette_getactive(p->gpd); + bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + gps->palcolor = palcolor; + strcpy(gps->colorname, palcolor->info); /* add stroke to frame */ BLI_addtail(&p->gpf->strokes, gps); @@ -761,12 +950,21 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, cons (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) { RegionView3D *rv3d = p->ar->regiondata; + bGPDlayer *gpl = p->gpl; + const int mval[2] = {x, y}; float mval_3d[3]; - + float fpt[3]; + + float diff_mat[4][4]; + /* calculate difference matrix if parent object */ + ED_gpencil_parent_location(gpl, diff_mat); + if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) { const float depth_mval = view3d_point_depth(rv3d, mval_3d); - const float depth_pt = view3d_point_depth(rv3d, &pt->x); + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + const float depth_pt = view3d_point_depth(rv3d, fpt); if (depth_pt > depth_mval) { return true; @@ -804,7 +1002,13 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, int pc1[2] = {0}; int pc2[2] = {0}; int i; - + float diff_mat[4][4]; + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + if (gps->totpoints == 0) { /* just free stroke */ if (gps->points) @@ -816,8 +1020,14 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, else if (gps->totpoints == 1) { /* only process if it hasn't been masked out... */ if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { - gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); - + if (gpl->parent == NULL) { + gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); + } + else { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + } /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { /* only check if point is inside */ @@ -826,7 +1036,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, // XXX: pressure sensitive eraser should apply here too? MEM_freeN(gps->points); if (gps->triangles) - MEM_freeN(gps->triangles); + MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); } } @@ -836,7 +1046,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* Pressure threshold at which stroke should be culled: Calculated as pressure value * below which we would have invisible strokes */ - const float cull_thresh = (gpl->thickness) ? 1.0f / ((float)gpl->thickness) : 1.0f; + const float cull_thresh = (gps->thickness) ? 1.0f / ((float)gps->thickness) : 1.0f; /* Amount to decrease the pressure of each point with each stroke */ // TODO: Fetch from toolsettings, or compute based on thickness instead? @@ -865,15 +1075,24 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* get points to work with */ pt1 = gps->points + i; pt2 = gps->points + i + 1; - + /* only process if it hasn't been masked out... */ if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) continue; - /* get coordinates of point in screenspace */ - gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); - gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); - + if (gpl->parent == NULL) { + gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); + gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); + } + else { + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); + } + /* Check that point segment of the boundbox of the eraser stroke */ if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) @@ -955,7 +1174,10 @@ static void gp_stroke_doeraser(tGPsdata *p) /* loop over strokes, checking segments for intersections */ for (gps = gpf->strokes.first; gps; gps = gpn) { gpn = gps->next; - + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } /* Not all strokes in the datablock may be valid in the current editor/context * (e.g. 2D space strokes in the 3D view, if the same datablock is shared) */ @@ -994,6 +1216,78 @@ static void gp_session_validatebuffer(tGPsdata *p) p->inittime = 0.0; } +/* create a new palette color */ +static bGPDpalettecolor *gp_create_new_color(bGPDpalette *palette) +{ + bGPDpalettecolor *palcolor; + + palcolor = gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + + return palcolor; +} + +/* initialize a drawing brush */ +static void gp_init_drawing_brush(ToolSettings *ts, tGPsdata *p) +{ + bGPDbrush *brush; + + /* if not exist, create a new one */ + if (BLI_listbase_is_empty(&ts->gp_brushes)) { + /* create new brushes */ + gpencil_brush_init_presets(ts); + brush = gpencil_brush_getactive(ts); + } + else { + /* Use the current */ + brush = gpencil_brush_getactive(ts); + } + /* be sure curves are initializated */ + curvemapping_initialize(brush->cur_sensitivity); + curvemapping_initialize(brush->cur_strength); + curvemapping_initialize(brush->cur_jitter); + + /* asign to temp tGPsdata */ + p->brush = brush; +} + + +/* initialize a paint palette brush and a default color if not exist */ +static void gp_init_palette(tGPsdata *p) +{ + bGPdata *gpd; + bGPDpalette *palette; + bGPDpalettecolor *palcolor; + + gpd = p->gpd; + + /* if not exist, create a new palette */ + if (BLI_listbase_is_empty(&gpd->palettes)) { + /* create new palette */ + palette = gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + /* now create a default color */ + palcolor = gp_create_new_color(palette); + } + else { + /* Use the current palette and color */ + palette = gpencil_palette_getactive(gpd); + /* the palette needs one color */ + if (BLI_listbase_is_empty(&palette->colors)) { + palcolor = gp_create_new_color(palette); + } + else { + palcolor = gpencil_palettecolor_getactive(palette); + } + /* in some situations can be null, so use first */ + if (palcolor == NULL) { + gpencil_palettecolor_setactive(palette, palette->colors.first); + palcolor = palette->colors.first; + } + } + + /* asign to temp tGPsdata */ + p->palettecolor = palcolor; +} + /* (re)init new painting data */ static bool gp_session_initdata(bContext *C, tGPsdata *p) { @@ -1158,7 +1452,16 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) /* clear out buffer (stored in gp-data), in case something contaminated it */ gp_session_validatebuffer(p); - + /* set brush and create a new one if null */ + gp_init_drawing_brush(ts, p); + /* set palette info and create a new one if null */ + gp_init_palette(p); + /* set palette colors */ + bGPDpalettecolor *palcolor = p->palettecolor; + bGPdata *pdata = p->gpd; + copy_v4_v4(pdata->scolor, palcolor->color); + pdata->sflag = palcolor->flag; + return 1; } @@ -1177,7 +1480,7 @@ static tGPsdata *gp_session_initpaint(bContext *C) * erase size won't get lost */ p->radius = U.gp_eraser; - + /* return context data for running paint operator */ return p; } @@ -1495,7 +1798,6 @@ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) /* ------------------------------- */ - static void gpencil_draw_exit(bContext *C, wmOperator *op) { tGPsdata *p = op->customdata; @@ -1513,7 +1815,7 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op) /* turn off radial brush cursor */ gpencil_draw_toggle_eraser_cursor(C, p, false); } - + /* always store the new eraser size to be used again next time * NOTE: Do this even when not in eraser mode, as eraser may * have been toggled at some point. @@ -1600,7 +1902,7 @@ static void gpencil_draw_status_indicators(tGPsdata *p) break; case GP_PAINTMODE_DRAW: ED_area_headerprint(p->sa, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | " - "ESC/Enter to end (or click outside this area)")); + "E/ESC/Enter to end (or click outside this area)")); break; case GP_PAINTMODE_DRAW_POLY: ED_area_headerprint(p->sa, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " @@ -1691,6 +1993,31 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event) */ p->mval[0] = event->mval[0] + 1; p->mval[1] = event->mval[1] + 1; + + /* verify key status for straight lines */ + if ((event->ctrl > 0) || (event->alt > 0)) { + if (p->straight[0] == 0) { + int dx = abs(p->mval[0] - p->mvalo[0]); + int dy = abs(p->mval[1] - p->mvalo[1]); + if ((dx > 0) || (dy > 0)) { + /* check mouse direction to replace the other coordinate with previous values */ + if (dx >= dy) { + /* horizontal */ + p->straight[0] = 1; + p->straight[1] = p->mval[1]; /* save y */ + } + else { + /* vertical */ + p->straight[0] = 2; + p->straight[1] = p->mval[0]; /* save x */ + } + } + } + } + else { + p->straight[0] = 0; + } + p->curtime = PIL_check_seconds_timer(); /* handle pressure sensitivity (which is supplied by tablets) */ @@ -1725,7 +2052,9 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event) p->mvalo[1] = p->mval[1]; p->opressure = p->pressure; p->inittime = p->ocurtime = p->curtime; - + p->straight[0] = 0; + p->straight[1] = 0; + /* special exception here for too high pressure values on first touch in * windows for some tablets, then we just skip first touch... */ @@ -1733,6 +2062,18 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event) return; } + /* check if alt key is pressed and limit to straight lines */ + if (p->straight[0] != 0) { + if (p->straight[0] == 1) { + /* horizontal */ + p->mval[1] = p->straight[1]; /* replace y */ + } + else { + /* vertical */ + p->mval[0] = p->straight[1]; /* replace x */ + } + } + /* fill in stroke data (not actually used directly by gpencil_draw_apply) */ RNA_collection_add(op->ptr, "stroke", &itemptr); @@ -1855,21 +2196,21 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event if (p->paintmode == GP_PAINTMODE_ERASER) { gpencil_draw_toggle_eraser_cursor(C, p, true); } - /* set cursor * NOTE: This may change later (i.e. intentionally via brush toggle, * or unintentionally if the user scrolls outside the area)... */ gpencil_draw_cursor_set(p); - + /* only start drawing immediately if we're allowed to do so... */ if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { /* hotkey invoked - start drawing */ /* printf("\tGP - set first spot\n"); */ p->status = GP_STATUS_PAINTING; - + /* handle the initial drawing - i.e. for just doing a simple dot */ gpencil_draw_apply_event(op, event); + op->flag |= OP_IS_MODAL_CURSOR_REGION; } else { /* toolbar invoked - don't start drawing yet... */ @@ -1976,6 +2317,10 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * is essential for ensuring that they can quickly return to that view */ } + else if ((ELEM(event->type, DKEY)) && (event->val == KM_RELEASE)) { + /* enable continuous if release D key in mid drawing */ + p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; + } else { estate = OPERATOR_RUNNING_MODAL; } @@ -1986,7 +2331,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* exit painting mode (and/or end current stroke) * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] */ - if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) { + if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) { /* exit() ends the current stroke before cleaning up */ /* printf("\t\tGP - end of paint op + end of stroke\n"); */ p->status = GP_STATUS_DONE; @@ -2045,6 +2390,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } else { /* printf("\t\tGP - end of stroke + op\n"); */ + /* disable paint session */ + p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON; + p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2074,6 +2422,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) in_bounds = true; } else { + /* disable paint session */ + p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON; + /* Out of bounds, or invalid in some other way */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; @@ -2090,6 +2441,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); } else { + /* disable paint session */ + p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON; + /* No region */ p->status = GP_STATUS_ERROR; estate = OPERATOR_CANCELLED; @@ -2117,6 +2471,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) p = gpencil_stroke_begin(C, op); if (p->status == GP_STATUS_ERROR) { + /* disable paint session */ + p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON; + estate = OPERATOR_CANCELLED; } } @@ -2125,6 +2482,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * NOTE: Don't eter this case if an error occurred while finding the * region (as above) */ + /* disable paint session */ + p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON; + p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index b6482786b4f..612b35aa608 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -43,6 +43,7 @@ #include "DNA_gpencil_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_gpencil.h" @@ -616,9 +617,10 @@ void GPENCIL_OT_select_less(wmOperatorType *ot) /* NOTE: Code here is adapted (i.e. copied directly) from gpencil_paint.c::gp_stroke_eraser_dostroke() * It would be great to de-duplicate the logic here sometime, but that can wait... */ -static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, - const int mx, const int my, const int radius, - const bool select, rcti *rect) +static bool gp_stroke_do_circle_sel( + bGPDstroke *gps, GP_SpaceConversion *gsc, + const int mx, const int my, const int radius, + const bool select, rcti *rect, const bool parented, float diff_mat[4][4]) { bGPDspoint *pt1, *pt2; int x0 = 0, y0 = 0, x1 = 0, y1 = 0; @@ -626,7 +628,14 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, bool changed = false; if (gps->totpoints == 1) { - gp_point_to_xy(gsc, gps, gps->points, &x0, &y0); + if (!parented) { + gp_point_to_xy(gsc, gps, gps->points, &x0, &y0); + } + else { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &x0, &y0); + } /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) { @@ -654,9 +663,18 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, /* get points to work with */ pt1 = gps->points + i; pt2 = gps->points + i + 1; - - gp_point_to_xy(gsc, gps, pt1, &x0, &y0); - gp_point_to_xy(gsc, gps, pt2, &x1, &y1); + if (!parented) { + gp_point_to_xy(gsc, gps, pt1, &x0, &y0); + gp_point_to_xy(gsc, gps, pt2, &x1, &y1); + } + else { + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &x0, &y0); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &x1, &y1); + } /* check that point segment of the boundbox of the selection stroke */ if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) || @@ -733,12 +751,14 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) /* find visible strokes, and select if hit */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { - changed |= gp_stroke_do_circle_sel(gps, &gsc, mx, my, radius, select, &rect); + changed |= gp_stroke_do_circle_sel( + gps, &gsc, mx, my, radius, select, &rect, + (gpl->parent != NULL), diff_mat); } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* updates */ if (changed) { WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); @@ -818,17 +838,25 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) WM_operator_properties_border_to_rcti(op, &rect); /* select/deselect points */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { + bGPDspoint *pt; int i; - + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int x0, y0; - + /* convert point coords to screenspace */ - gp_point_to_xy(&gsc, gps, pt, &x0, &y0); - + if (gpl->parent == NULL) { + gp_point_to_xy(&gsc, gps, pt, &x0, &y0); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0); + } + /* test if in selection rect */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0)) { if (select) { @@ -837,16 +865,16 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) else { pt->flag &= ~GP_SPOINT_SELECT; } - + changed = true; } } - + /* Ensure that stroke selection is in sync with its points */ gpencil_stroke_sync_selection(gps); } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* updates */ if (changed) { WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); @@ -920,20 +948,26 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) } /* select/deselect points */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { bGPDspoint *pt; int i; - + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int x0, y0; - + /* convert point coords to screenspace */ - gp_point_to_xy(&gsc, gps, pt, &x0, &y0); - + if (gpl->parent == NULL) { + gp_point_to_xy(&gsc, gps, pt, &x0, &y0); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0); + } /* test if in lasso boundbox + within the lasso noose */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0) && - BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX)) + BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX)) { if (select) { pt->flag |= GP_SPOINT_SELECT; @@ -941,16 +975,16 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) else { pt->flag &= ~GP_SPOINT_SELECT; } - + changed = true; } } - + /* Ensure that stroke selection is in sync with its points */ gpencil_stroke_sync_selection(gps); } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* cleanup */ MEM_freeN((void *)mcords); @@ -1020,35 +1054,42 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) /* First Pass: Find stroke point which gets hit */ /* XXX: maybe we should go from the top of the stack down instead... */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { bGPDspoint *pt; int i; - + /* firstly, check for hit-point */ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int xy[2]; - - gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]); - + + if (gpl->parent == NULL) { + gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]); + } + /* do boundbox check first */ if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) { const int pt_distance = len_manhattan_v2v2_int(mval, xy); - + /* check if point is inside */ if (pt_distance <= radius_squared) { /* only use this point if it is a better match than the current hit - T44685 */ if (pt_distance < hit_distance) { hit_stroke = gps; - hit_point = pt; + hit_point = pt; hit_distance = pt_distance; } } } } } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* Abort if nothing hit... */ if (ELEM(NULL, hit_stroke, hit_point)) { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index c39a3aa5cfc..1d7582eb18b 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -43,7 +43,9 @@ #include "BKE_blender_undo.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_gpencil.h" +#include "BKE_main.h" #include "ED_gpencil.h" @@ -140,7 +142,7 @@ void gpencil_undo_push(bGPdata *gpd) */ undo_node->gpd->adt = NULL; - BKE_gpencil_free(undo_node->gpd); + BKE_gpencil_free(undo_node->gpd, false); MEM_freeN(undo_node->gpd); BLI_freelinkN(&undo_nodes, undo_node); @@ -151,7 +153,7 @@ void gpencil_undo_push(bGPdata *gpd) /* create new undo node */ undo_node = MEM_callocN(sizeof(bGPundonode), "gpencil undo node"); - undo_node->gpd = gpencil_data_duplicate(gpd, true); + undo_node->gpd = gpencil_data_duplicate(G.main, gpd, true); cur_node = undo_node; @@ -168,7 +170,7 @@ void gpencil_undo_finish(void) */ undo_node->gpd->adt = NULL; - BKE_gpencil_free(undo_node->gpd); + BKE_gpencil_free(undo_node->gpd, false); MEM_freeN(undo_node->gpd); undo_node = undo_node->next; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index d62625baaa4..f2c542da0aa 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -17,7 +17,7 @@ * * The Original Code is Copyright (C) 2014, Blender Foundation * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -32,9 +32,13 @@ #include <stddef.h> #include <math.h> +#include "MEM_guardedalloc.h" + #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" +#include "BLI_rand.h" #include "DNA_gpencil_types.h" #include "DNA_object_types.h" @@ -46,6 +50,7 @@ #include "BKE_context.h" #include "BKE_gpencil.h" #include "BKE_tracking.h" +#include "BKE_action.h" #include "WM_api.h" @@ -269,6 +274,34 @@ int gp_active_layer_poll(bContext *C) return (gpl != NULL); } +/* poll callback for checking if there is an active brush */ +int gp_active_brush_poll(bContext *C) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = gpencil_brush_getactive(ts); + + 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 = 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 = gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + + return (palcolor != NULL); +} + /* ******************************************************** */ /* Dynamic Enums of GP Layers */ /* NOTE: These include an option to create a new layer and use that... */ @@ -412,6 +445,60 @@ bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) return ED_gpencil_stroke_can_use_direct(sa, gps); } +/* Check whether given stroke can be edited for the current color */ +bool ED_gpencil_stroke_color_use(const bGPDlayer *gpl, const bGPDstroke *gps) +{ + /* check if the color is editable */ + bGPDpalettecolor *palcolor = gps->palcolor; + if (palcolor != NULL) { + if (palcolor->flag & PC_COLOR_HIDE) + return false; + if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) + return false; + } + + 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 = gpencil_palette_getactive(gpd); + if (palette == NULL) { + palette = gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + } + /* get color */ + palcolor = gpencil_palettecolor_getbyname(palette, gps->colorname); + if (palcolor == NULL) { + if (gps->palcolor == NULL) { + palcolor = 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 = 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 */ @@ -451,6 +538,50 @@ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) } } +/* convert point to parent space */ +void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt) +{ + float fpt[3]; + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + copy_v3_v3(&r_pt->x, fpt); +} + +/* Change position relative to parent object */ +void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps) +{ + bGPDspoint *pt; + int i; + + /* undo matrix */ + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + float fpt[3]; + + ED_gpencil_parent_location(gpl, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + for (i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); + copy_v3_v3(&pt->x, fpt); + } +} + +/* Change point position relative to parent object */ +void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt) +{ + /* undo matrix */ + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + float fpt[3]; + + ED_gpencil_parent_location(gpl, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); + copy_v3_v3(&pt->x, fpt); +} /* Convert Grease Pencil points to screen-space values * WARNING: This assumes that the caller has already checked whether the stroke in question can be drawn @@ -591,25 +722,107 @@ bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure) 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 } } /* Based on influence factor, blend between original and optimal smoothed coordinate */ interp_v3_v3v3(&pt->x, &pt->x, sco, inf); +#if 0 + /* XXX: Disabled because get weird result */ if (affect_pressure) { pt->pressure = pressure; } +#endif return true; } /** +* Apply smooth for strength to stroke point +* \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) +{ + bGPDspoint *ptb = &gps->points[i]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = i - 1; + int after = i + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* 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); + const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength; + + /* Based on influence factor, blend between original and optimal */ + ptb->strength = (1.0f - inf) * ptb->strength + inf * optimal; + + return true; +} + +/** +* Apply smooth for thickness to stroke point (use pressure) +* \param gps Stroke to smooth +* \param i Point index +* \param inf Amount of smoothing to apply +*/ +bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf) +{ + bGPDspoint *ptb = &gps->points[i]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = i - 1; + int after = i + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the pressure + * at the distance of point b + */ + 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 */ + ptb->pressure = (1.0f - inf) * ptb->pressure + inf * optimal; + + return true; +} + +/** * Subdivide a stroke once, by adding a point half way between each pair of existing points * \param gps Stroke data * \param new_totpoints Total number of points (after subdividing) @@ -633,16 +846,99 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) interp_v3_v3v3(&pt->x, &prev->x, &next->x, 0.5f); pt->pressure = interpf(prev->pressure, next->pressure, 0.5f); - pt->time = interpf(prev->time, next->time, 0.5f); + pt->strength = interpf(prev->strength, next->strength, 0.5f); + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = interpf(prev->time, next->time, 0.5f); } /* Update to new total number of points */ gps->totpoints = new_totpoints; } -/* ******************************************************** */ +/** + * Add randomness to stroke + * \param gps Stroke data + * \param brsuh Brush data + */ +void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush) +{ + bGPDspoint *pt1, *pt2, *pt3; + float v1[3]; + float v2[3]; + if (gps->totpoints < 3) { + return; + } + + /* get two vectors using 3 points */ + pt1 = &gps->points[0]; + pt2 = &gps->points[1]; + pt3 = &gps->points[(int)(gps->totpoints * 0.75)]; + + sub_v3_v3v3(v1, &pt2->x, &pt1->x); + sub_v3_v3v3(v2, &pt3->x, &pt2->x); + normalize_v3(v1); + normalize_v3(v2); + + /* get normal vector to plane created by two vectors */ + float normal[3]; + cross_v3_v3v3(normal, v1, v2); + normalize_v3(normal); + /* get orthogonal vector to plane to rotate random effect */ + float ortho[3]; + cross_v3_v3v3(ortho, v1, normal); + normalize_v3(ortho); + /* Read all points and apply shift vector (first and last point not modified) */ + for (int i = 1; i < gps->totpoints - 1; ++i) { + bGPDspoint *pt = &gps->points[i]; + /* get vector with shift (apply a division because random is too sensitive */ + const float fac = BLI_frand() * (brush->draw_random_sub / 10.0f); + float svec[3]; + copy_v3_v3(svec, ortho); + if (BLI_frand() > 0.5f) { + mul_v3_fl(svec, -fac); + } + else { + mul_v3_fl(svec, fac); + } + + /* apply shift */ + 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) { + unit_m4(diff_mat); + return; + } + else { + if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { + mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); + return; + } + else if (gpl->partype == PARBONE) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, gpl->parsubstr); + if (pchan) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, ob->obmat, pchan->pose_mat); + mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse); + } + else { + mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); /* if bone not found use object (armature) */ + } + return; + } + else { + unit_m4(diff_mat); /* not defined type */ + } + } +} +/* ******************************************************** */ bool ED_gpencil_stroke_minmax( const bGPDstroke *gps, const bool use_select, float r_min[3], float r_max[3]) @@ -659,3 +955,74 @@ bool ED_gpencil_stroke_minmax( } return changed; } +/* Dynamic Enums of GP Brushes */ + +EnumPropertyItem *ED_gpencil_brushes_enum_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush; + EnumPropertyItem *item = NULL, item_tmp = { 0 }; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, ts)) { + return DummyRNA_DEFAULT_items; + } + + /* Existing brushes */ + for (brush = ts->gp_brushes.first; brush; brush = brush->next, i++) { + item_tmp.identifier = brush->info; + item_tmp.name = brush->info; + item_tmp.value = i; + + if (brush->flag & GP_BRUSH_ACTIVE) + item_tmp.icon = ICON_BRUSH_DATA; + else + item_tmp.icon = ICON_NONE; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} +/* Dynamic Enums of GP Palettes */ + +EnumPropertyItem *ED_gpencil_palettes_enum_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDpalette *palette; + EnumPropertyItem *item = NULL, item_tmp = { 0 }; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, gpd)) { + return DummyRNA_DEFAULT_items; + } + + /* Existing palettes */ + for (palette = gpd->palettes.first; palette; palette = palette->next, i++) { + item_tmp.identifier = palette->info; + item_tmp.name = palette->info; + item_tmp.value = i; + + if (palette->flag & PL_PALETTE_ACTIVE) + item_tmp.icon = ICON_COLOR; + else + item_tmp.icon = ICON_NONE; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} +/* ******************************************************** */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 27e1051a336..0940f594482 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -74,6 +74,7 @@ typedef struct bAnimContext { short mode; /* editor->mode */ short spacetype; /* sa->spacetype */ short regiontype; /* active region -> type (channels or main) */ + struct ScrArea *sa; /* editor host */ struct SpaceLink *sl; /* editor data */ struct ARegion *ar; /* region within editor */ @@ -85,6 +86,8 @@ typedef struct bAnimContext { ListBase *markers; /* active set of markers */ struct ReportList *reports; /* pointer to current reports list */ + + float yscale_fac; /* scale factor for height of channels (i.e. based on the size of keyframes) */ } bAnimContext; /* Main Data container types */ @@ -331,11 +334,11 @@ typedef enum eAnimFilter_Flags { /* -------------- Channel Defines -------------- */ /* channel heights */ -#define ACHANNEL_FIRST (-0.8f * U.widget_unit) -#define ACHANNEL_HEIGHT (0.8f * U.widget_unit) -#define ACHANNEL_HEIGHT_HALF (0.4f * U.widget_unit) -#define ACHANNEL_SKIP (0.1f * U.widget_unit) -#define ACHANNEL_STEP (ACHANNEL_HEIGHT + ACHANNEL_SKIP) +#define ACHANNEL_FIRST(ac) (-0.8f * (ac)->yscale_fac * U.widget_unit) +#define ACHANNEL_HEIGHT(ac) (0.8f * (ac)->yscale_fac * U.widget_unit) +#define ACHANNEL_HEIGHT_HALF(ac) (0.4f * (ac)->yscale_fac * U.widget_unit) +#define ACHANNEL_SKIP (0.1f * U.widget_unit) +#define ACHANNEL_STEP(ac) (ACHANNEL_HEIGHT(ac) + ACHANNEL_SKIP) /* channel widths */ #define ACHANNEL_NAMEWIDTH (10 * U.widget_unit) @@ -347,7 +350,6 @@ typedef enum eAnimFilter_Flags { /* -------------- NLA Channel Defines -------------- */ /* NLA channel heights */ -// XXX: NLACHANNEL_FIRST isn't used? #define NLACHANNEL_FIRST (-0.8f * U.widget_unit) #define NLACHANNEL_HEIGHT(snla) ((snla && (snla->flag & SNLA_NOSTRIPCURVES)) ? (0.8f * U.widget_unit) : (1.2f * U.widget_unit)) #define NLACHANNEL_HEIGHT_HALF(snla) ((snla && (snla->flag & SNLA_NOSTRIPCURVES)) ? (0.4f * U.widget_unit) : (0.6f * U.widget_unit)) diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index de5ab80a88f..d526b0841cc 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -41,6 +41,8 @@ struct bGPdata; struct bGPDlayer; struct bGPDframe; struct bGPDstroke; +struct bGPDpalette; +struct bGPDpalettecolor; struct bAnimContext; struct KeyframeEditData; struct PointerRNA; @@ -57,6 +59,7 @@ struct wmKeyConfig; typedef struct tGPspoint { int x, y; /* x and y coordinates of cursor (in relative to area) */ float pressure; /* pressure of tablet at this point */ + float strength; /* pressure of tablet at this point for alpha factor */ float time; /* Time relative to stroke start (used when converting to path) */ } tGPspoint; @@ -86,6 +89,9 @@ bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfr bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *sa, const struct bGPDstroke *gps); bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps); +bool ED_gpencil_stroke_color_use(const struct bGPDlayer *gpl, const struct bGPDstroke *gps); + +struct bGPDpalettecolor *ED_gpencil_stroke_getcolor(struct bGPdata *gpd, struct bGPDstroke *gps); bool ED_gpencil_stroke_minmax( const struct bGPDstroke *gps, const bool use_select, @@ -142,4 +148,10 @@ bool ED_gpencil_anim_copybuf_paste(struct bAnimContext *ac, const short copy_mod int ED_gpencil_session_active(void); int ED_undo_gpencil_step(struct bContext *C, int step, const char *name); +/* ------------ Transformation Utilities ------------ */ + +/* get difference matrix using parent */ +void ED_gpencil_parent_location(struct bGPDlayer *gpl, float diff_mat[4][4]); + + #endif /* __ED_GPENCIL_H__ */ diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index 7d163da0db0..b1f3f012e09 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -80,7 +80,7 @@ typedef struct ActKeyBlock { /* key-block info */ char sel; - short handle_type; + short flag; float val; float start, end; @@ -89,6 +89,12 @@ typedef struct ActKeyBlock { short totcurve; } ActKeyBlock; +/* ActKeyBlock - Flag */ +typedef enum eActKeyBlock_Flag { + /* Key block represents a moving hold */ + ACTKEYBLOCK_FLAG_MOVING_HOLD = (1 << 0), +} eActKeyBlock_Flag; + /* *********************** Keyframe Drawing ****************************** */ /* options for keyframe shape drawing */ @@ -108,23 +114,23 @@ void draw_keyframe_shape(float x, float y, float xscale, float hsize, short sel, /* Channel Drawing ------------------ */ /* F-Curve */ -void draw_fcurve_channel(struct View2D *v2d, struct AnimData *adt, struct FCurve *fcu, float ypos); +void draw_fcurve_channel(struct View2D *v2d, struct AnimData *adt, struct FCurve *fcu, float ypos, float yscale_fac); /* Action Group Summary */ -void draw_agroup_channel(struct View2D *v2d, struct AnimData *adt, struct bActionGroup *agrp, float ypos); +void draw_agroup_channel(struct View2D *v2d, struct AnimData *adt, struct bActionGroup *agrp, float ypos, float yscale_fac); /* Action Summary */ -void draw_action_channel(struct View2D *v2d, struct AnimData *adt, struct bAction *act, float ypos); +void draw_action_channel(struct View2D *v2d, struct AnimData *adt, struct bAction *act, float ypos, float yscale_fac); /* Object Summary */ -void draw_object_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Object *ob, float ypos); +void draw_object_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Object *ob, float ypos, float yscale_fac); /* Scene Summary */ -void draw_scene_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Scene *sce, float ypos); +void draw_scene_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Scene *sce, float ypos, float yscale_fac); /* DopeSheet Summary */ -void draw_summary_channel(struct View2D *v2d, struct bAnimContext *ac, float ypos); +void draw_summary_channel(struct View2D *v2d, struct bAnimContext *ac, float ypos, float yscale_fac); /* Grease Pencil datablock summary */ -void draw_gpencil_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPdata *gpd, float ypos); +void draw_gpencil_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPdata *gpd, float ypos, float yscale_fac); /* Grease Pencil Layer */ -void draw_gpl_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos); +void draw_gpl_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos, float yscale_fac); /* Mask Layer */ -void draw_masklay_channel(struct View2D *v2d, struct bDopeSheet *ads, struct MaskLayer *masklay, float ypos); +void draw_masklay_channel(struct View2D *v2d, struct bDopeSheet *ads, struct MaskLayer *masklay, float ypos, float yscale_fac); /* Keydata Generation --------------- */ /* F-Curve */ diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 81e2558e765..602e203a381 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -325,18 +325,18 @@ bool ANIM_driver_vars_paste(struct ReportList *reports, struct FCurve *fcu, bool /* Notes: * - All the defines for this (User-Pref settings and Per-Scene settings) * are defined in DNA_userdef_types.h - * - Scene settings take presidence over those for userprefs, with old files + * - Scene settings take precedence over those for userprefs, with old files * inheriting userpref settings for the scene settings * - "On/Off + Mode" are stored per Scene, but "settings" are currently stored * as userprefs */ /* Auto-Keying macros for use by various tools */ -/* check if auto-keyframing is enabled (per scene takes presidence) */ +/* check if auto-keyframing is enabled (per scene takes precedence) */ #define IS_AUTOKEY_ON(scene) ((scene) ? (scene->toolsettings->autokey_mode & AUTOKEY_ON) : (U.autokey_mode & AUTOKEY_ON)) -/* check the mode for auto-keyframing (per scene takes presidence) */ +/* check the mode for auto-keyframing (per scene takes precedence) */ #define IS_AUTOKEY_MODE(scene, mode) ((scene) ? (scene->toolsettings->autokey_mode == AUTOKEY_MODE_##mode) : (U.autokey_mode == AUTOKEY_MODE_##mode)) -/* check if a flag is set for auto-keyframing (per scene takes presidence) */ +/* check if a flag is set for auto-keyframing (per scene takes precedence) */ #define IS_AUTOKEY_FLAG(scene, flag) \ ((scene) ? \ ((scene->toolsettings->autokey_flag & AUTOKEY_FLAG_##flag) || (U.autokey_flag & AUTOKEY_FLAG_##flag)) \ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 8bd8a14030f..422836b5be2 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -208,6 +208,7 @@ eV3DProjStatus ED_view3d_project_float_global(const struct ARegion *ar, const fl eV3DProjStatus ED_view3d_project_float_object(const struct ARegion *ar, const float co[3], float r_co[2], const eV3DProjTest flag); float ED_view3d_calc_zfac(const struct RegionView3D *rv3d, const float co[3], bool *r_flip); +bool ED_view3d_clip_segment(const struct RegionView3D *rv3d, float ray_start[3], float ray_end[3]); bool ED_view3d_win_to_ray( const struct ARegion *ar, const struct View3D *v3d, const float mval[2], float ray_start[3], float ray_normal[3], const bool do_clip); @@ -218,6 +219,7 @@ void ED_view3d_global_to_vector(const struct RegionView3D *rv3d, const float coo void ED_view3d_win_to_3d(const struct ARegion *ar, const float depth_pt[3], const float mval[2], float out[3]); void ED_view3d_win_to_3d_int(const struct ARegion *ar, const float depth_pt[3], const int mval[2], float out[3]); void ED_view3d_win_to_delta(const struct ARegion *ar, const float mval[2], float out[3], const float zfac); +void ED_view3d_win_to_origin(const struct ARegion *ar, const float mval[2], float out[3]); void ED_view3d_win_to_vector(const struct ARegion *ar, const float mval[2], float out[3]); bool ED_view3d_win_to_segment(const struct ARegion *ar, struct View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], const bool do_clip); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index 2c80701bf69..e016e014a1a 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -320,9 +320,9 @@ DEF_ICON(OUTLINER_OB_SPEAKER) DEF_ICON(BLANK123) DEF_ICON(BLANK124) DEF_ICON(BLANK125) - DEF_ICON(BLANK126) - DEF_ICON(BLANK127) #endif +DEF_ICON(RESTRICT_COLOR_OFF) +DEF_ICON(RESTRICT_COLOR_ON) DEF_ICON(RESTRICT_VIEW_OFF) DEF_ICON(RESTRICT_VIEW_ON) DEF_ICON(RESTRICT_SELECT_OFF) @@ -1020,6 +1020,7 @@ DEF_VICO(KEYTYPE_KEYFRAME_VEC) DEF_VICO(KEYTYPE_BREAKDOWN_VEC) DEF_VICO(KEYTYPE_EXTREME_VEC) DEF_VICO(KEYTYPE_JITTER_VEC) +DEF_VICO(KEYTYPE_MOVING_HOLD_VEC) DEF_VICO(COLORSET_01_VEC) DEF_VICO(COLORSET_02_VEC) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index a623f5cfb9c..f77e795adca 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -382,7 +382,11 @@ typedef bool (*uiMenuStepFunc)(struct bContext *C, int direction, void *arg1); typedef struct uiPopupMenu uiPopupMenu; -struct uiPopupMenu *UI_popup_menu_begin(struct bContext *C, const char *title, int icon) ATTR_NONNULL(); +uiPopupMenu *UI_popup_menu_begin( + struct bContext *C, const char *title, int icon) ATTR_NONNULL(); +uiPopupMenu *UI_popup_menu_begin_ex( + struct bContext *C, const char *title, const char *block_name, + int icon) ATTR_NONNULL(); void UI_popup_menu_end(struct bContext *C, struct uiPopupMenu *head); struct uiLayout *UI_popup_menu_layout(uiPopupMenu *head); diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index dc843952229..a81221ed54b 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -347,6 +347,8 @@ void UI_GetThemeColorShade3ubv(int colorid, int offset, unsigned char col[3]) // get four color values, scaled to 0.0-1.0 range void UI_GetThemeColor4fv(int colorid, float col[4]); +// get four color values, range 0.0-1.0, complete with shading offset for the RGB components +void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4]); // get the 3 or 4 byte values void UI_GetThemeColor3ubv(int colorid, unsigned char col[3]); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index ba7240be5d8..cbe8654ceb6 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1138,7 +1138,7 @@ static bool ui_but_event_property_operator_string(const bContext *C, uiBut *but, * as new items are added to the menu later on. It also optimises efficiency - * a radial menu is best kept symmetrical, with as large an angle between * items as possible, so that the gestural mouse movements can be fast and inexact. - + * * It starts off with two opposite sides for the first two items * then joined by the one below for the third (this way, even with three items, * the menu seems to still be 'in order' reading left to right). Then the fourth is diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 72a6a04feec..9ce863dc8f7 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -734,24 +734,50 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), CLAMP(max, rect.ymin, rect.ymax); fdrawline(rect.xmax - 3, min, rect.xmax - 3, max); } + /* RGB (3 channel) */ + else if (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB) { + glBlendFunc(GL_ONE, GL_ONE); + + glEnableClientState(GL_VERTEX_ARRAY); + + glPushMatrix(); + + glTranslatef(rect.xmin, yofs, 0.f); + glScalef(w, h, 0.f); + + glColor3fv( colors_alpha[0] ); + glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_1); + glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); + + glColor3fv( colors_alpha[1] ); + glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_2); + glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); - /* RGB / YCC (3 channels) */ + glColor3fv( colors_alpha[2] ); + glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_3); + glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); + + glDisableClientState(GL_VERTEX_ARRAY); + glPopMatrix(); + } + /* PARADE / YCC (3 channels) */ else if (ELEM(scopes->wavefrm_mode, - SCOPES_WAVEFRM_RGB, + SCOPES_WAVEFRM_RGB_PARADE, SCOPES_WAVEFRM_YCC_601, SCOPES_WAVEFRM_YCC_709, - SCOPES_WAVEFRM_YCC_JPEG)) + SCOPES_WAVEFRM_YCC_JPEG + )) { - int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB); - + int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB_PARADE); + glBlendFunc(GL_ONE, GL_ONE); - + glPushMatrix(); glEnableClientState(GL_VERTEX_ARRAY); - + glTranslatef(rect.xmin, yofs, 0.f); glScalef(w3, h, 0.f); - + glColor3fv((rgb) ? colors_alpha[0] : colorsycc_alpha[0]); glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_1); glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); @@ -760,19 +786,19 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), glColor3fv((rgb) ? colors_alpha[1] : colorsycc_alpha[1]); glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_2); glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); - + glTranslatef(1.f, 0.f, 0.f); glColor3fv((rgb) ? colors_alpha[2] : colorsycc_alpha[2]); glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_3); glDrawArrays(GL_POINTS, 0, scopes->waveform_tot); - + glDisableClientState(GL_VERTEX_ARRAY); glPopMatrix(); - - - /* min max */ + } + /* min max */ + if (scopes->wavefrm_mode != SCOPES_WAVEFRM_LUMA ) { for (int c = 0; c < 3; c++) { - if (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB) + if (ELEM(scopes->wavefrm_mode, SCOPES_WAVEFRM_RGB_PARADE, SCOPES_WAVEFRM_RGB)) glColor3f(colors[c][0] * 0.75f, colors[c][1] * 0.75f, colors[c][2] * 0.75f); else glColor3f(colorsycc[c][0] * 0.75f, colorsycc[c][1] * 0.75f, colorsycc[c][2] * 0.75f); diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index 2cbc56b14d3..ac7b6428217 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -805,7 +805,7 @@ static int depthdropper_init(bContext *C, wmOperator *op) RegionView3D *rv3d = CTX_wm_region_view3d(C); if (rv3d && rv3d->persp == RV3D_CAMOB) { View3D *v3d = CTX_wm_view3d(C); - if (v3d->camera && v3d->camera->data && (((ID *)v3d->camera->data)->lib == NULL)) { + if (v3d->camera && v3d->camera->data && !ID_IS_LINKED_DATABLOCK(v3d->camera->data)) { RNA_id_pointer_create(v3d->camera->data, &ddr->ptr); ddr->prop = RNA_struct_find_property(&ddr->ptr, "dof_distance"); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index e0b8003d8a9..79831cefde6 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1057,7 +1057,9 @@ static void ui_multibut_free(uiHandleButtonData *data, uiBlock *block) } } -static bool ui_multibut_states_tag(uiBut *but_active, uiHandleButtonData *data, const wmEvent *event) +static bool ui_multibut_states_tag( + uiBut *but_active, + uiHandleButtonData *data, const wmEvent *event) { uiBut *but; float seg[2][2]; @@ -1668,7 +1670,9 @@ static bool ui_but_contains_point_px_icon(uiBut *but, ARegion *ar, const wmEvent return BLI_rcti_isect_pt(&rect, x, y); } -static bool ui_but_drag_init(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static bool ui_but_drag_init( + bContext *C, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { /* prevent other WM gestures to start while we try to drag */ WM_gestures_remove(C); @@ -1740,11 +1744,16 @@ static bool ui_but_drag_init(bContext *C, uiBut *but, uiHandleButtonData *data, } } else { - wmDrag *drag; + wmDrag *drag = WM_event_start_drag( + C, but->icon, but->dragtype, but->dragpoin, + ui_but_value_get(but), WM_DRAG_NOP); - drag = WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_but_value_get(but), WM_DRAG_NOP); - if (but->imb) - WM_event_drag_image(drag, but->imb, but->imb_scale, BLI_rctf_size_x(&but->rect), BLI_rctf_size_y(&but->rect)); + if (but->imb) { + WM_event_drag_image( + drag, but->imb, but->imb_scale, + BLI_rctf_size_x(&but->rect), + BLI_rctf_size_y(&but->rect)); + } } return true; } @@ -1872,11 +1881,15 @@ static void ui_but_smart_controller_add(bContext *C, uiBut *from, uiBut *to) /* (3) add a new controller */ if (WM_operator_name_call(C, "LOGIC_OT_controller_add", WM_OP_EXEC_DEFAULT, &props_ptr) & OPERATOR_FINISHED) { cont = (bController *)ob->controllers.last; - cont->type = CONT_LOGIC_AND; /* Quick fix to make sure we always have an AND controller. It might be nicer to make sure the operator gives us the right one though... */ + /* Quick fix to make sure we always have an AND controller. + * It might be nicer to make sure the operator gives us the right one though... */ + cont->type = CONT_LOGIC_AND; /* (4) link the sensor->controller->actuator */ tmp_but = MEM_callocN(sizeof(uiBut), "uiBut"); - UI_but_link_set(tmp_but, (void **)&cont, (void ***)&(cont->links), &(cont->totlinks), from->link->tocode, (int)to->hardmin); + UI_but_link_set( + tmp_but, (void **)&cont, (void ***)&(cont->links), + &cont->totlinks, from->link->tocode, (int)to->hardmin); tmp_but->hardmin = from->link->tocode; tmp_but->poin = (char *)cont; @@ -3180,7 +3193,9 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa } -static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static void ui_do_but_textedit( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int retval = WM_UI_HANDLER_CONTINUE; bool changed = false, inbox = false, update = false; @@ -3233,7 +3248,8 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle if (data->searchbox) inbox = ui_searchbox_inside(data->searchbox, event->x, event->y); - /* for double click: we do a press again for when you first click on button (selects all text, no cursor pos) */ + /* for double click: we do a press again for when you first click on button + * (selects all text, no cursor pos) */ if (event->val == KM_PRESS || event->val == KM_DBL_CLICK) { float mx, my; @@ -3477,7 +3493,9 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle ED_region_tag_redraw(data->region); } -static void ui_do_but_textedit_select(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static void ui_do_but_textedit_select( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my, retval = WM_UI_HANDLER_CONTINUE; @@ -3682,7 +3700,9 @@ static uiBut *ui_but_list_row_text_activate( /* ***************** events for different button types *************** */ -static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_BUT( + bContext *C, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE && event->val == KM_PRESS) { @@ -3713,7 +3733,9 @@ static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, cons return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_HOTKEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_HOTKEYEVT( + bContext *C, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { @@ -3776,7 +3798,9 @@ static int ui_do_but_HOTKEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_KEYEVT( + bContext *C, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { @@ -3802,7 +3826,9 @@ static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, c return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_TEX( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { if (ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, PADENTER, RETKEY) && event->val == KM_PRESS) { @@ -3830,7 +3856,9 @@ static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButton return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_SEARCH_UNLINK(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_SEARCH_UNLINK( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { uiButExtraIconType extra_icon_type; @@ -3877,7 +3905,9 @@ static int ui_do_but_SEARCH_UNLINK(bContext *C, uiBlock *block, uiBut *but, uiHa return ui_do_but_TEX(C, block, but, data, event); } -static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_TOG( + bContext *C, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { #ifdef USE_DRAG_TOGGLE if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -3944,7 +3974,9 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_EXIT( + bContext *C, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -4092,7 +4124,7 @@ static float ui_numedit_apply_snap( static bool ui_numedit_but_NUM( uiBut *but, uiHandleButtonData *data, - int mx, + int mx, const bool is_motion, const enum eSnapType snap, float fac) { float deler, tempf, softmin, softmax, softrange; @@ -4100,8 +4132,10 @@ static bool ui_numedit_but_NUM( bool changed = false; const bool is_float = ui_but_is_float(but); - /* prevent unwanted drag adjustments */ - if (ui_but_dragedit_update_mval(data, mx) == false) { + /* prevent unwanted drag adjustments, test motion so modifier keys refresh. */ + if ((is_motion || data->draglock) && + (ui_but_dragedit_update_mval(data, mx) == false)) + { return changed; } @@ -4242,7 +4276,9 @@ static bool ui_numedit_but_NUM( return changed; } -static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_NUM( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my; /* mouse location scaled to fit the UI */ int screen_mx, screen_my; /* mouse location kept at screen pixel coords */ @@ -4324,6 +4360,7 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton } } else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) { + const bool is_motion = (event->type == MOUSEMOVE); const enum eSnapType snap = ui_event_to_snap(event); float fac; @@ -4335,8 +4372,9 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton fac = 1.0f; if (event->shift) fac /= 10.0f; - if (ui_numedit_but_NUM(but, data, (ui_but_is_cursor_warp(but) ? screen_mx : mx), snap, fac)) + if (ui_numedit_but_NUM(but, data, (ui_but_is_cursor_warp(but) ? screen_mx : mx), is_motion, snap, fac)) { ui_numedit_apply(C, block, but, data); + } #ifdef USE_DRAG_MULTINUM else if (data->multi_data.has_mbuts) { if (data->multi_data.init == BUTTON_MULTI_INIT_ENABLE) { @@ -4430,7 +4468,7 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton static bool ui_numedit_but_SLI( uiBut *but, uiHandleButtonData *data, - int mx, const bool is_horizontal, + int mx, const bool is_horizontal, const bool is_motion, const bool snap, const bool shift) { float deler, f, tempf, softmin, softmax, softrange; @@ -4440,8 +4478,9 @@ static bool ui_numedit_but_SLI( /* note, 'offs' is really from the widget drawing rounded corners see 'widget_numslider' */ float offs; - /* prevent unwanted drag adjustments */ + /* prevent unwanted drag adjustments, test motion so modifier keys refresh. */ if ((but->type != UI_BTYPE_SCROLL) && + (is_motion || data->draglock) && (ui_but_dragedit_update_mval(data, mx) == false)) { return changed; @@ -4541,7 +4580,9 @@ static bool ui_numedit_but_SLI( return changed; } -static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_SLI( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my, click = 0; int retval = WM_UI_HANDLER_CONTINUE; @@ -4634,12 +4675,14 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton } } else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) { + const bool is_motion = (event->type == MOUSEMOVE); #ifdef USE_DRAG_MULTINUM data->multi_data.drag_dir[0] += abs(data->draglastx - mx); data->multi_data.drag_dir[1] += abs(data->draglasty - my); #endif - if (ui_numedit_but_SLI(but, data, mx, true, event->ctrl != 0, event->shift != 0)) + if (ui_numedit_but_SLI(but, data, mx, true, is_motion, event->ctrl != 0, event->shift != 0)) { ui_numedit_apply(C, block, but, data); + } #ifdef USE_DRAG_MULTINUM else if (data->multi_data.has_mbuts) { @@ -4722,7 +4765,9 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton return retval; } -static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_SCROLL( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my /*, click = 0 */; int retval = WM_UI_HANDLER_CONTINUE; @@ -4765,8 +4810,10 @@ static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleBut button_activate_state(C, but, BUTTON_STATE_EXIT); } else if (event->type == MOUSEMOVE) { - if (ui_numedit_but_SLI(but, data, (horizontal) ? mx : my, horizontal, false, false)) + const bool is_motion = (event->type == MOUSEMOVE); + if (ui_numedit_but_SLI(but, data, (horizontal) ? mx : my, horizontal, is_motion, false, false)) { ui_numedit_apply(C, block, but, data); + } } retval = WM_UI_HANDLER_BREAK; @@ -4775,7 +4822,9 @@ static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleBut return retval; } -static int ui_do_but_GRIP(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_GRIP( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my; int retval = WM_UI_HANDLER_CONTINUE; @@ -4826,7 +4875,9 @@ static int ui_do_but_GRIP(bContext *C, uiBlock *block, uiBut *but, uiHandleButto return retval; } -static int ui_do_but_LISTROW(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_LISTROW( + bContext *C, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { /* hack to pass on ctrl+click and double click to overlapping text @@ -4846,7 +4897,9 @@ static int ui_do_but_LISTROW(bContext *C, uiBut *but, uiHandleButtonData *data, return ui_do_but_EXIT(C, but, data, event); } -static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_BLOCK( + bContext *C, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5021,7 +5074,9 @@ static void ui_palette_set_active(uiBut *but) } } -static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_COLOR( + bContext *C, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { /* first handle click on icondrag type button */ @@ -5154,7 +5209,9 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_UNITVEC(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_UNITVEC( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my; @@ -5431,7 +5488,9 @@ static void ui_ndofedit_but_HSVCUBE( ui_but_v3_set(but, data->vec); } -static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_HSVCUBE( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my; @@ -5607,8 +5666,7 @@ static bool ui_numedit_but_HSVCIRCLE( ui_color_picker_to_rgb_v(hsv, rgb); if ((but->flag & UI_BUT_VEC_SIZE_LOCK) && (rgb[0] || rgb[1] || rgb[2])) { - normalize_v3(rgb); - mul_v3_fl(rgb, but->a2); + normalize_v3_length(rgb, but->a2); } if (use_display_colorspace) @@ -5684,8 +5742,7 @@ static void ui_ndofedit_but_HSVCIRCLE( ui_color_picker_to_rgb_v(hsv, data->vec); if ((but->flag & UI_BUT_VEC_SIZE_LOCK) && (data->vec[0] || data->vec[1] || data->vec[2])) { - normalize_v3(data->vec); - mul_v3_fl(data->vec, but->a2); + normalize_v3_length(data->vec, but->a2); } if (use_display_colorspace) @@ -5695,7 +5752,9 @@ static void ui_ndofedit_but_HSVCIRCLE( } -static int ui_do_but_HSVCIRCLE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_HSVCIRCLE( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { ColorPicker *cpicker = but->custom_data; float *hsv = cpicker->color_data; @@ -5823,7 +5882,9 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m return changed; } -static int ui_do_but_COLORBAND(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_COLORBAND( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { ColorBand *coba; CBData *cbd; @@ -5999,7 +6060,9 @@ static bool ui_numedit_but_CURVE( return changed; } -static int ui_do_but_CURVE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_CURVE( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my, a; bool changed = false; @@ -6167,7 +6230,9 @@ static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int m return changed; } -static int ui_do_but_HISTOGRAM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_HISTOGRAM( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my; @@ -6240,7 +6305,9 @@ static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx return changed; } -static int ui_do_but_WAVEFORM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_WAVEFORM( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my; @@ -6294,7 +6361,9 @@ static int ui_do_but_WAVEFORM(bContext *C, uiBlock *block, uiBut *but, uiHandleB return WM_UI_HANDLER_CONTINUE; } -static int ui_do_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_LINK( + bContext *C, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { VECCOPY2D(but->linkto, event->mval); @@ -6357,7 +6426,9 @@ static bool ui_numedit_but_TRACKPREVIEW( return changed; } -static int ui_do_but_TRACKPREVIEW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) +static int ui_do_but_TRACKPREVIEW( + bContext *C, uiBlock *block, uiBut *but, + uiHandleButtonData *data, const wmEvent *event) { int mx, my; @@ -6853,7 +6924,8 @@ static bool ui_but_menu(bContext *C, uiBut *but) WM_operator_properties_create(&ptr_props, "WM_OT_doc_view"); RNA_string_set(&ptr_props, "doc_id", buf); - uiItemFullO(layout, "WM_OT_doc_view", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"), + uiItemFullO(layout, "WM_OT_doc_view", + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"), ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0); /* XXX inactive option, not for public! */ @@ -6862,7 +6934,8 @@ static bool ui_but_menu(bContext *C, uiBut *but) RNA_string_set(&ptr_props, "doc_id", buf); RNA_string_set(&ptr_props, "doc_new", RNA_property_description(but->rnaprop)); - uiItemFullO(layout, "WM_OT_doc_edit", "Submit Description", ICON_NONE, ptr_props.data, WM_OP_INVOKE_DEFAULT, 0); + uiItemFullO(layout, "WM_OT_doc_edit", + "Submit Description", ICON_NONE, ptr_props.data, WM_OP_INVOKE_DEFAULT, 0); #endif } } @@ -7442,7 +7515,8 @@ static bool ui_but_contains_point_px(ARegion *ar, uiBut *but, int x, int y) /** * Can we mouse over the button or is it hidden/disabled/layout. - * Note: ctrl is kind of a hack currently, so that non-embossed UI_BTYPE_TEXT button behaves as a label when ctrl is not pressed. + * \note ctrl is kind of a hack currently, + * so that non-embossed UI_BTYPE_TEXT button behaves as a label when ctrl is not pressed. */ static bool ui_but_is_interactive(const uiBut *but, const bool labeledit) { @@ -7636,7 +7710,9 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s if (but->block->auto_open == true) { /* test for toolbox */ time = 1; } - else if ((but->block->flag & UI_BLOCK_LOOP && but->type != UI_BTYPE_BLOCK) || but->block->auto_open == true) { + else if ((but->block->flag & UI_BLOCK_LOOP && but->type != UI_BTYPE_BLOCK) || + (but->block->auto_open == true)) + { time = 5 * U.menuthreshold2; } else if (U.uiflag & USER_MENUOPENAUTO) { @@ -8722,8 +8798,7 @@ static bool ui_mouse_motion_towards_check( float delta[2]; sub_v2_v2v2(delta, oldp, cent); - normalize_v2(delta); - mul_v2_fl(delta, MENU_TOWARDS_WIGGLE_ROOM); + normalize_v2_length(delta, MENU_TOWARDS_WIGGLE_ROOM); add_v2_v2(oldp, delta); } @@ -10081,6 +10156,10 @@ static void ui_popup_handler_remove(bContext *C, void *userdata) { uiPopupBlockHandle *menu = userdata; + if (menu->cancel_func) { + menu->cancel_func(C, menu->popup_arg); + } + /* free menu block if window is closed for some reason */ ui_popup_block_free(C, menu); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 6dc60f1d70b..4107414a240 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -505,6 +505,11 @@ static void vicon_keytype_jitter_draw(int x, int y, int w, int h, float alpha) vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_JITTER); } +static void vicon_keytype_moving_hold_draw(int x, int y, int w, int h, float alpha) +{ + vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_MOVEHOLD); +} + static void vicon_colorset_draw(int index, int x, int y, int w, int h, float UNUSED(alpha)) { bTheme *btheme = UI_GetTheme(); @@ -792,6 +797,7 @@ static void init_internal_icons(void) def_internal_vicon(VICO_KEYTYPE_BREAKDOWN_VEC, vicon_keytype_breakdown_draw); def_internal_vicon(VICO_KEYTYPE_EXTREME_VEC, vicon_keytype_extreme_draw); def_internal_vicon(VICO_KEYTYPE_JITTER_VEC, vicon_keytype_jitter_draw); + def_internal_vicon(VICO_KEYTYPE_MOVING_HOLD_VEC, vicon_keytype_moving_hold_draw); def_internal_vicon(VICO_COLORSET_01_VEC, vicon_colorset_draw_01); def_internal_vicon(VICO_COLORSET_02_VEC, vicon_colorset_draw_02); @@ -1055,6 +1061,9 @@ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size) } } +static void ui_id_preview_image_render_size( + const bContext *C, Scene *scene, ID *id, PreviewImage *pi, int size, const bool use_job); + void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool big) { Icon *icon = BKE_icon_get(icon_id); @@ -1070,22 +1079,20 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi } if (di) { - if (di->type == ICON_TYPE_PREVIEW) { - PreviewImage *prv = (icon->type != 0) ? BKE_previewimg_id_ensure((ID *)icon->obj) : icon->obj; - - if (prv) { - const int size = big ? ICON_SIZE_PREVIEW : ICON_SIZE_ICON; - - if (!prv->use_deferred || prv->rect[size] || (prv->flag[size] & PRV_USER_EDITED)) { - return; + switch (di->type) { + case ICON_TYPE_PREVIEW: + { + ID *id = (icon->type != 0) ? icon->obj : NULL; + PreviewImage *prv = id ? BKE_previewimg_id_ensure(id) : icon->obj; + + if (prv) { + const int size = big ? ICON_SIZE_PREVIEW : ICON_SIZE_ICON; + + if (id || prv->use_deferred) { + ui_id_preview_image_render_size(C, NULL, id, prv, size, true); + } } - - icon_create_rect(prv, size); - - /* Always using job (background) version. */ - ED_preview_icon_job(C, prv, NULL, prv->rect[size], prv->w[size], prv->h[size]); - - prv->flag[size] &= ~PRV_CHANGED; + break; } } } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index f02aad1ff87..8336efa381b 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -548,6 +548,8 @@ struct uiPopupBlockHandle { /* store data for refreshing popups */ struct uiPopupBlockCreate popup_create_vars; + /* true if we can re-create the popup using 'popup_create_vars' */ + bool can_refresh; struct wmTimer *scrolltimer; diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index ff29a6f8e33..1af6d902b18 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -416,7 +416,7 @@ bool UI_context_copy_to_selected_list( if ((id_data == NULL) || (id_data->tag & LIB_TAG_DOIT) == 0 || - (id_data->lib) || + ID_IS_LINKED_DATABLOCK(id_data) || (GS(id_data->name) != id_code)) { BLI_remlink(&lb, link); diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index d4d3e1af1fd..c621fcf493d 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -110,7 +110,7 @@ bool ui_but_menu_step_poll(const uiBut *but) { BLI_assert(but->type == UI_BTYPE_MENU); - /* currenly only RNA buttons */ + /* currently only RNA buttons */ return ((but->menu_step_func != NULL) || (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM)); } @@ -454,7 +454,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) if (but->rnapoin.id.data) { ID *id = but->rnapoin.id.data; - if (id->lib) { + if (ID_IS_LINKED_DATABLOCK(id)) { BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Library: %s"), id->lib->name); data->format[data->totline].color_id = UI_TIP_LC_NORMAL; data->totline++; @@ -1701,7 +1701,9 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar) ar->do_draw &= ~RGN_DRAW_REFRESH_UI; for (block = ar->uiblocks.first; block; block = block_next) { block_next = block->next; - ui_popup_block_refresh((bContext *)C, block->handle, NULL, NULL); + if (block->handle->can_refresh) { + ui_popup_block_refresh((bContext *)C, block->handle, NULL, NULL); + } } } @@ -1811,6 +1813,8 @@ uiBlock *ui_popup_block_refresh( bContext *C, uiPopupBlockHandle *handle, ARegion *butregion, uiBut *but) { + BLI_assert(handle->can_refresh == true); + const int margin = UI_POPUP_MARGIN; wmWindow *window = CTX_wm_window(C); ARegion *ar = handle->region; @@ -2001,6 +2005,8 @@ uiPopupBlockHandle *ui_popup_block_create( handle->popup_create_vars.arg = arg; handle->popup_create_vars.butregion = but ? butregion : NULL; copy_v2_v2_int(handle->popup_create_vars.event_xy, &window->eventstate->x); + /* caller may free vars used to create this popup, in that case this variable should be disabled. */ + handle->can_refresh = true; /* create area region */ ar = ui_region_temp_add(CTX_wm_screen(C)); @@ -2800,6 +2806,7 @@ uiPopupBlockHandle *ui_popup_menu_create( WM_event_add_mousemove(C); } + handle->can_refresh = false; MEM_freeN(pup); return handle; @@ -2807,14 +2814,17 @@ uiPopupBlockHandle *ui_popup_menu_create( /******************** Popup Menu API with begin and end ***********************/ -/* only return handler, and set optional title */ -uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon) +/** + * Only return handler, and set optional title. + * \param block_name: Assigned to uiBlock.name (useful info for debugging). + */ +uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, const char *title, const char *block_name, int icon) { uiStyle *style = UI_style_get_dpi(); uiPopupMenu *pup = MEM_callocN(sizeof(uiPopupMenu), "popup menu"); uiBut *but; - pup->block = UI_block_begin(C, NULL, __func__, UI_EMBOSS_PULLDOWN); + pup->block = UI_block_begin(C, NULL, block_name, UI_EMBOSS_PULLDOWN); pup->block->flag |= UI_BLOCK_POPUP_MEMORY | UI_BLOCK_IS_FLIP; pup->block->puphash = ui_popup_menu_hash(title); pup->layout = UI_block_layout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, MENU_PADDING, style); @@ -2845,6 +2855,11 @@ uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon) return pup; } +uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon) +{ + return UI_popup_menu_begin_ex(C, title, __func__, icon); +} + /* set the whole structure to work */ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) { @@ -2860,7 +2875,8 @@ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) UI_popup_handlers_add(C, &window->modalhandlers, menu, 0); WM_event_add_mousemove(C); - + + menu->can_refresh = false; MEM_freeN(pup); } @@ -2991,6 +3007,7 @@ void UI_pie_menu_end(bContext *C, uiPieMenu *pie) menu, WM_HANDLER_ACCEPT_DBL_CLICK); WM_event_add_mousemove(C); + menu->can_refresh = false; MEM_freeN(pie); } @@ -3203,7 +3220,8 @@ void UI_popup_menu_reports(bContext *C, ReportList *reports) if (pup == NULL) { char title[UI_MAX_DRAW_STR]; BLI_snprintf(title, sizeof(title), "%s: %s", IFACE_("Report"), report->typestr); - pup = UI_popup_menu_begin(C, title, ICON_NONE); + /* popup_menu stuff does just what we need (but pass meaningful block name) */ + pup = UI_popup_menu_begin_ex(C, title, __func__, ICON_NONE); layout = UI_popup_menu_layout(pup); } else { @@ -3331,6 +3349,11 @@ void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block) if (win) { UI_popup_handlers_remove(&win->modalhandlers, block->handle); ui_popup_block_free(C, block->handle); + + /* In the case we have nested popups, closing one may need to redraw another, see: T48874 */ + for (ARegion *ar = win->screen->regionbase.first; ar; ar = ar->next) { + ED_region_tag_refresh_ui(ar); + } } } } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 7f276bcc634..58cadf5587a 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -302,7 +302,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) break; case UI_ID_LOCAL: if (id) { - if (id_make_local(id, false)) { + if (id_make_local(CTX_data_main(C), id, false, false)) { /* reassign to get get proper updates/notifiers */ idptr = RNA_property_pointer_get(&template->ptr, template->prop); RNA_property_pointer_set(&template->ptr, template->prop, idptr); @@ -455,7 +455,7 @@ static void template_ID( else { but = uiDefIconBut(block, UI_BTYPE_BUT, 0, ICON_LIBRARY_DATA_DIRECT, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0, 0, 0, 0, TIP_("Direct linked library datablock, click to make local")); - if (!id_make_local(id, true /* test */) || (idfrom && idfrom->lib)) + if (!id_make_local(CTX_data_main(C), id, true /* test */, false) || (idfrom && idfrom->lib)) UI_but_flag_enable(but, UI_BUT_DISABLED); } @@ -475,7 +475,7 @@ static void template_ID( UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template), SET_INT_IN_POINTER(UI_ID_ALONE)); if (/* test only */ - (id_copy(id, NULL, true) == false) || + (id_copy(CTX_data_main(C), id, NULL, true) == false) || (idfrom && idfrom->lib) || (!editable) || /* object in editmode - don't change data */ @@ -974,7 +974,7 @@ static uiLayout *draw_modifier( } UI_block_lock_clear(block); - UI_block_lock_set(block, ob && ob->id.lib, ERROR_LIBDATA_MESSAGE); + UI_block_lock_set(block, ob && ID_IS_LINKED_DATABLOCK(ob), ERROR_LIBDATA_MESSAGE); if (!ELEM(md->type, eModifierType_Fluidsim, eModifierType_Softbody, eModifierType_ParticleSystem, eModifierType_Cloth, eModifierType_Smoke)) @@ -1021,7 +1021,7 @@ uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr) return NULL; } - UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ob->id.lib), ERROR_LIBDATA_MESSAGE); + UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED_DATABLOCK(ob)), ERROR_LIBDATA_MESSAGE); /* find modifier and draw it */ cageIndex = modifiers_getCageIndex(scene, ob, &lastCageIndex, 0); @@ -1249,7 +1249,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr) return NULL; } - UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ob->id.lib), ERROR_LIBDATA_MESSAGE); + UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED_DATABLOCK(ob)), ERROR_LIBDATA_MESSAGE); /* hrms, the temporal constraint should not draw! */ if (con->type == CONSTRAINT_TYPE_KINEMATIC) { @@ -1611,7 +1611,7 @@ void uiTemplateColorRamp(uiLayout *layout, PointerRNA *ptr, const char *propname block = uiLayoutAbsoluteBlock(layout); id = cptr.id.data; - UI_block_lock_set(block, (id && id->lib), ERROR_LIBDATA_MESSAGE); + UI_block_lock_set(block, (id && ID_IS_LINKED_DATABLOCK(id)), ERROR_LIBDATA_MESSAGE); colorband_buttons_layout(layout, block, cptr.data, &rect, cb, expand); @@ -1940,6 +1940,7 @@ enum { UICURVE_FUNC_RESET_VIEW, UICURVE_FUNC_HANDLE_VECTOR, UICURVE_FUNC_HANDLE_AUTO, + UICURVE_FUNC_HANDLE_AUTO_ANIM, UICURVE_FUNC_EXTEND_HOZ, UICURVE_FUNC_EXTEND_EXP, }; @@ -1960,13 +1961,16 @@ static void curvemap_tools_dofunc(bContext *C, void *cumap_v, int event) cumap->curr = cumap->clipr; break; case UICURVE_FUNC_HANDLE_VECTOR: /* set vector */ - curvemap_sethandle(cuma, 1); + curvemap_handle_set(cuma, HD_VECT); curvemapping_changed(cumap, false); break; case UICURVE_FUNC_HANDLE_AUTO: /* set auto */ - curvemap_sethandle(cuma, 0); + curvemap_handle_set(cuma, HD_AUTO); curvemapping_changed(cumap, false); break; + case UICURVE_FUNC_HANDLE_AUTO_ANIM: /* set auto-clamped */ + curvemap_handle_set(cuma, HD_AUTO_ANIM); + curvemapping_changed(cumap, false); case UICURVE_FUNC_EXTEND_HOZ: /* extend horiz */ cuma->flag &= ~CUMA_EXTEND_EXTRAPOLATE; curvemapping_changed(cumap, false); @@ -2000,6 +2004,9 @@ static uiBlock *curvemap_tools_func( uiDefIconTextBut( block, UI_BTYPE_BUT_MENU, 1, ICON_BLANK1, IFACE_("Auto Handle"), 0, yco -= UI_UNIT_Y, menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_HANDLE_AUTO, ""); + uiDefIconTextBut( + block, UI_BTYPE_BUT_MENU, 1, ICON_BLANK1, IFACE_("Auto Clamped Handle"), + 0, yco -= UI_UNIT_Y, menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_HANDLE_AUTO_ANIM, ""); } if (show_extend) { @@ -2263,7 +2270,7 @@ void uiTemplateCurveMapping( cb->prop = prop; id = cptr.id.data; - UI_block_lock_set(block, (id && id->lib), ERROR_LIBDATA_MESSAGE); + UI_block_lock_set(block, (id && ID_IS_LINKED_DATABLOCK(id)), ERROR_LIBDATA_MESSAGE); curvemap_buttons_layout(layout, &cptr, type, levels, brush, neg_slope, cb); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 5098e701638..02981b543f3 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -3042,7 +3042,7 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { /* Now we reduce alpha of the inner color (i.e. the color shown) - * so that this setting can look greyed out, while retaining + * so that this setting can look grayed out, while retaining * the checkboard (for transparent values). This is needed * here as the effects of ui_widget_color_disabled() are overwritten. */ diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index e2e2413c717..c8ff335f2a0 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -1011,6 +1011,8 @@ void ui_theme_init_default(void) rgba_char_args_set(btheme->tact.keyborder, 0, 0, 0, 255); rgba_char_args_set(btheme->tact.keyborder_select, 0, 0, 0, 255); + btheme->tact.keyframe_scale_fac = 1.0f; + /* space nla */ btheme->tnla = btheme->tact; @@ -1471,6 +1473,30 @@ void UI_GetThemeColor3ubv(int colorid, unsigned char col[3]) col[2] = cp[2]; } +/* get the color, range 0.0-1.0, complete with shading offset */ +void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4]) +{ + int r, g, b, a; + const unsigned char *cp; + + cp = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid); + + r = offset + (int) cp[0]; + CLAMP(r, 0, 255); + g = offset + (int) cp[1]; + CLAMP(g, 0, 255); + b = offset + (int) cp[2]; + CLAMP(b, 0, 255); + + a = (int) cp[3]; /* no shading offset... */ + CLAMP(a, 0, 255); + + col[0] = ((float)r) / 255.0f; + col[1] = ((float)g) / 255.0f; + col[2] = ((float)b) / 255.0f; + col[3] = ((float)a) / 255.0f; +} + /* get the color, in char pointer */ void UI_GetThemeColor4ubv(int colorid, unsigned char col[4]) { @@ -2699,6 +2725,14 @@ void init_userdef_do_versions(void) } } } + + if (!USER_VERSION_ATLEAST(277, 2)) { + bTheme *btheme; + for (btheme = U.themes.first; btheme; btheme = btheme->next) { + if (btheme->tact.keyframe_scale_fac < 0.1f) + btheme->tact.keyframe_scale_fac = 1.0f; + } + } /** * Include next version bump. diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index b74c4b5f526..d62651cef81 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -175,7 +175,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) open_sim); if (export_count == 0) { - BKE_report(op->reports, RPT_WARNING, "Export file is empty"); + BKE_report(op->reports, RPT_WARNING, "No objects selected -- Created empty export file"); return OPERATOR_CANCELLED; } else if (export_count < 0) { diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index 1dfb1cddd2c..d4c49833c2c 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -287,8 +287,7 @@ static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op) short a; /* dvec */ - normalize_v3_v3(dvec, rv3d->persinv[2]); - mul_v3_fl(dvec, offs); + normalize_v3_v3_length(dvec, rv3d->persinv[2], offs); /* base correction */ copy_m3_m4(bmat, obedit->obmat); diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 69588928253..281a8b2a02d 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -563,7 +563,7 @@ static BMEdge *bm_face_split_edge_find( if (UNLIKELY(BM_edge_exists(v_pivot, l_iter->e->v1) || BM_edge_exists(v_pivot, l_iter->e->v2))) { - /* very unlikley but will cause complications splicing the verts together, + /* very unlikely but will cause complications splicing the verts together, * so just skip this case */ ok = false; } diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index 8868827a11f..e31e4096ded 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -153,8 +153,7 @@ static float edbm_rip_edge_side_measure( ED_view3d_project_float_v2_m4(ar, e->v2->co, e_v2_co, projectMat); sub_v2_v2v2(vec, cent, mid); - normalize_v2(vec); - mul_v2_fl(vec, 0.01f); + normalize_v2_length(vec, 0.01f); /* rather then adding to both verts, subtract from the mouse */ sub_v2_v2v2(fmval_tweak, fmval, vec); @@ -396,7 +395,7 @@ static void edbm_ripsel_deselect_helper(BMesh *bm, EdgeLoopPair *eloop_pairs, * return an un-ordered array of loop pairs * use for rebuilding face-fill * - * \note the method currenly used fails for edges with 3+ face users and gives + * \note the method currently used fails for edges with 3+ face users and gives * nasty holes in the mesh, there isnt a good way of knowing ahead of time * which loops will be split apart (its possible to figure out but quite involved). * So for now this is a known limitation of current rip-fill option. @@ -749,10 +748,8 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve } if (do_fill) { - if (do_fill) { - /* match extrude vert-order */ - BM_edge_create(bm, vout[1], vout[0], NULL, BM_CREATE_NOP); - } + /* match extrude vert-order */ + BM_edge_create(bm, vout[1], vout[0], NULL, BM_CREATE_NOP); } MEM_freeN(vout); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 9f1602ccfaf..1a14fad8650 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -49,6 +49,7 @@ #include "BKE_material.h" #include "BKE_context.h" +#include "BKE_deform.h" #include "BKE_depsgraph.h" #include "BKE_report.h" #include "BKE_texture.h" @@ -79,6 +80,8 @@ #include "mesh_intern.h" /* own include */ +#include "bmesh_tools.h" + #define USE_FACE_CREATE_SEL_EXTEND static int edbm_subdivide_exec(bContext *C, wmOperator *op) @@ -3292,7 +3295,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) Object *ob = base_iter->object; if (ob->type == OB_MESH) { Mesh *me = ob->data; - if (me->id.lib == NULL) { + if (!ID_IS_LINKED_DATABLOCK(me)) { BMesh *bm_old = NULL; int retval_iter = 0; @@ -3979,6 +3982,196 @@ void MESH_OT_tris_convert_to_quads(wmOperatorType *ot) join_triangle_props(ot); } + +/* -------------------------------------------------------------------- */ + +/** \name Decimate + * + * \note The function to decimate is intended for use as a modifier, + * while its handy allow access as a tool - this does cause access to be a little awkward + * (passing selection as weights for eg). + * + * \{ */ + +static int edbm_decimate_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + const float ratio = RNA_float_get(op->ptr, "ratio"); + bool use_vertex_group = RNA_boolean_get(op->ptr, "use_vertex_group"); + const float vertex_group_factor = RNA_float_get(op->ptr, "vertex_group_factor"); + const bool invert_vertex_group = RNA_boolean_get(op->ptr, "invert_vertex_group"); + const bool use_symmetry = RNA_boolean_get(op->ptr, "use_symmetry"); + const float symmetry_eps = 0.00002f; + const int symmetry_axis = use_symmetry ? RNA_enum_get(op->ptr, "symmetry_axis") : -1; + + /* nop */ + if (ratio == 1.0f) { + return OPERATOR_FINISHED; + } + + float *vweights = MEM_mallocN(sizeof(*vweights) * bm->totvert, __func__); + { + const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); + const int defbase_act = obedit->actdef - 1; + + if (use_vertex_group && (cd_dvert_offset == -1)) { + BKE_report(op->reports, RPT_WARNING, "No active vertex group"); + use_vertex_group = false; + } + + BMIter iter; + BMVert *v; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + float weight = 0.0f; + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + if (use_vertex_group) { + const MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset); + weight = defvert_find_weight(dv, defbase_act); + if (invert_vertex_group) { + weight = 1.0f - weight; + } + } + else { + weight = 1.0f; + } + } + + vweights[i] = weight; + BM_elem_index_set(v, i); /* set_inline */ + } + bm->elem_index_dirty &= ~BM_VERT; + } + + float ratio_adjust; + + if ((bm->totface == bm->totfacesel) || (ratio == 0.0f)) { + ratio_adjust = ratio; + } + else { + /** + * Calculate a new ratio based on faces that could be remoevd during decimation. + * needed so 0..1 has a meaningful range when operating on the selection. + * + * This doesn't have to be totally accurate, + * but needs to be greater than the number of selected faces + */ + + int totface_basis = 0; + int totface_adjacent = 0; + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + /* count faces during decimation, ngons are triangulated */ + const int f_len = f->len > 4 ? (f->len - 2) : 1; + totface_basis += f_len; + + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (vweights[BM_elem_index_get(l_iter->v)] != 0.0f) { + totface_adjacent += f_len; + break; + } + } while ((l_iter = l_iter->next) != l_first); + } + + ratio_adjust = ratio; + ratio_adjust = 1.0f - ratio_adjust; + ratio_adjust *= (float)totface_adjacent / (float)totface_basis; + ratio_adjust = 1.0f - ratio_adjust; + } + + BM_mesh_decimate_collapse( + em->bm, ratio_adjust, vweights, vertex_group_factor, false, + symmetry_axis, symmetry_eps); + + MEM_freeN(vweights); + + { + short selectmode = em->selectmode; + if ((selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) { + /* ensure we flush edges -> faces */ + selectmode |= SCE_SELECT_EDGE; + } + EDBM_selectmode_flush_ex(em, selectmode); + } + + EDBM_update_generic(em, true, true); + + return OPERATOR_FINISHED; +} + + +static bool edbm_decimate_check(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return true; +} + + +static void edbm_decimate_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout, *box, *row, *col; + PointerRNA ptr; + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + + uiItemR(layout, &ptr, "ratio", 0, NULL, ICON_NONE); + + box = uiLayoutBox(layout); + uiItemR(box, &ptr, "use_vertex_group", 0, NULL, ICON_NONE); + col = uiLayoutColumn(box, false); + uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_vertex_group")); + uiItemR(col, &ptr, "vertex_group_factor", 0, NULL, ICON_NONE); + uiItemR(col, &ptr, "invert_vertex_group", 0, NULL, ICON_NONE); + + box = uiLayoutBox(layout); + uiItemR(box, &ptr, "use_symmetry", 0, NULL, ICON_NONE); + row = uiLayoutRow(box, true); + uiLayoutSetActive(row, RNA_boolean_get(&ptr, "use_symmetry")); + uiItemR(row, &ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE); +} + + +void MESH_OT_decimate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Decimate Geometry"; + ot->idname = "MESH_OT_decimate"; + ot->description = "Simplify geometry by collapsing edges"; + + /* api callbacks */ + ot->exec = edbm_decimate_exec; + ot->check = edbm_decimate_check; + ot->ui = edbm_decimate_ui; + ot->poll = ED_operator_editmesh; + + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Note, keep in sync with 'rna_def_modifier_decimate' */ + RNA_def_float(ot->srna, "ratio", 1.0f, 0.0f, 1.0f, "Ratio", "", 0.0f, 1.0f); + + RNA_def_boolean(ot->srna, "use_vertex_group", false, "Vertex Group", + "Use active vertex group as an influence"); + RNA_def_float(ot->srna, "vertex_group_factor", 1.0f, 0.0f, 1000.0f, "Weight", + "Vertex group strength", 0.0f, 10.0f); + RNA_def_boolean(ot->srna, "invert_vertex_group", false, "Invert", + "Invert vertex group influence"); + + RNA_def_boolean(ot->srna, "use_symmetry", false, "Symmetry", + "Maintain symmetry on an axis"); + + RNA_def_enum(ot->srna, "symmetry_axis", rna_enum_axis_xyz_items, 1, "Axis", "Axis of symmetry"); +} + +/** \} */ + + /* -------------------------------------------------------------------- */ /* Dissolve */ diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index b44fbc3ce45..c9814d189a4 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -114,7 +114,7 @@ static struct { TaskPool *task_pool; #endif -} um_arraystore = {NULL}; +} um_arraystore = {{NULL}}; static void um_arraystore_cd_compact( struct CustomData *cdata, const size_t data_len, diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 5101608246a..4fc61e0912e 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -1440,13 +1440,9 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree, BMEdge *e, ARegion *ar, View3D *v sub_v3_v3v3(dir2, origin, co2); sub_v3_v3v3(dir3, origin, co3); - normalize_v3(dir1); - normalize_v3(dir2); - normalize_v3(dir3); - - mul_v3_fl(dir1, epsilon); - mul_v3_fl(dir2, epsilon); - mul_v3_fl(dir3, epsilon); + normalize_v3_length(dir1, epsilon); + normalize_v3_length(dir2, epsilon); + normalize_v3_length(dir3, epsilon); /* offset coordinates slightly along view vectors, to avoid * hitting the faces that own the edge.*/ diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index e0ddc017e93..772bb1bd308 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -511,7 +511,7 @@ static int layers_poll(bContext *C) { Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib); + return (ob && !ID_IS_LINKED_DATABLOCK(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED_DATABLOCK(data)); } static int mesh_uv_texture_add_exec(bContext *C, wmOperator *UNUSED(op)) @@ -759,7 +759,7 @@ static int mesh_customdata_mask_clear_poll(bContext *C) return false; } - if (me->id.lib == NULL) { + if (!ID_IS_LINKED_DATABLOCK(me)) { CustomData *data = GET_CD_DATA(me, vdata); if (CustomData_has_layer(data, CD_PAINT_MASK)) { return true; @@ -813,7 +813,7 @@ static int mesh_customdata_skin_state(bContext *C) if (ob && ob->type == OB_MESH) { Mesh *me = ob->data; - if (me->id.lib == NULL) { + if (!ID_IS_LINKED_DATABLOCK(me)) { CustomData *data = GET_CD_DATA(me, vdata); return CustomData_has_layer(data, CD_MVERT_SKIN); } diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 08b4d1aee45..300b21a052d 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -216,6 +216,7 @@ void MESH_OT_fill_holes(struct wmOperatorType *ot); void MESH_OT_beautify_fill(struct wmOperatorType *ot); void MESH_OT_quads_convert_to_tris(struct wmOperatorType *ot); void MESH_OT_tris_convert_to_quads(struct wmOperatorType *ot); +void MESH_OT_decimate(struct wmOperatorType *ot); void MESH_OT_dissolve_verts(struct wmOperatorType *ot); void MESH_OT_dissolve_edges(struct wmOperatorType *ot); void MESH_OT_dissolve_faces(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 2853ae3a84c..697a92f36d1 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -106,6 +106,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_beautify_fill); WM_operatortype_append(MESH_OT_quads_convert_to_tris); WM_operatortype_append(MESH_OT_tris_convert_to_quads); + WM_operatortype_append(MESH_OT_decimate); WM_operatortype_append(MESH_OT_dissolve_verts); WM_operatortype_append(MESH_OT_dissolve_edges); WM_operatortype_append(MESH_OT_dissolve_faces); diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 1b2d4b7c130..b26989113d4 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -172,7 +172,7 @@ int join_mesh_exec(bContext *C, wmOperator *op) */ if (key) { /* make a duplicate copy that will only be used here... (must remember to free it!) */ - nkey = BKE_key_copy(key); + nkey = BKE_key_copy(bmain, key); /* for all keys in old block, clear data-arrays */ for (kb = key->block.first; kb; kb = kb->next) { @@ -540,7 +540,7 @@ int join_mesh_exec(bContext *C, wmOperator *op) if (matmap) MEM_freeN(matmap); /* other mesh users */ - test_object_materials(bmain, (ID *)me); + test_all_objects_materials(bmain, (ID *)me); /* free temp copy of destination shapekeys (if applicable) */ if (nkey) { @@ -564,21 +564,10 @@ int join_mesh_exec(bContext *C, wmOperator *op) BKE_key_sort(key); } - DAG_relations_tag_update(bmain); // removed objects, need to rebuild dag -#if 0 - ED_object_editmode_enter(C, EM_WAITCURSOR); - ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR | EM_DO_UNDO); -#else - /* toggle editmode using lower level functions so this can be called from python */ - EDBM_mesh_make(scene->toolsettings, ob, false); - EDBM_mesh_load(ob); - EDBM_mesh_free(me->edit_btmesh); - MEM_freeN(me->edit_btmesh); - me->edit_btmesh = NULL; DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA); -#endif + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); return OPERATOR_FINISHED; diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 08ed9813456..57e08d36f85 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -48,6 +48,7 @@ #include "DNA_scene_types.h" #include "DNA_vfont_types.h" #include "DNA_actuator_types.h" +#include "DNA_gpencil_types.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -1149,10 +1150,22 @@ static int object_delete_exec(bContext *C, wmOperator *op) } else if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1) { BKE_reportf(op->reports, RPT_WARNING, - "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", - base->object->id.name + 2, scene->id.name + 2); + "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", + base->object->id.name + 2, scene->id.name + 2); continue; } + /* remove from Grease Pencil parent */ + for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) { + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl->parent != NULL) { + Object *ob = gpl->parent; + Object *curob = base->object; + if (ob == curob) { + gpl->parent = NULL; + } + } + } + } /* deselect object -- it could be used in other scenes */ base->object->flag &= ~SELECT; @@ -1166,7 +1179,7 @@ static int object_delete_exec(bContext *C, wmOperator *op) Base *base_other; for (scene_iter = bmain->scene.first; scene_iter; scene_iter = scene_iter->id.next) { - if (scene_iter != scene && !(scene_iter->id.lib)) { + if (scene_iter != scene && !ID_IS_LINKED_DATABLOCK(scene_iter)) { base_other = BKE_scene_base_find(scene_iter, base->object); if (base_other) { if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1) { @@ -1303,7 +1316,7 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base, for (dob = lb->first; dob; dob = dob->next) { Base *basen; - Object *ob = BKE_object_copy(dob->ob); + Object *ob = BKE_object_copy(bmain, dob->ob); /* font duplis can have a totcol without material, we get them from parent * should be implemented better... @@ -1519,11 +1532,12 @@ static int convert_poll(bContext *C) Object *obact = CTX_data_active_object(C); Scene *scene = CTX_data_scene(C); - return (!scene->id.lib && obact && scene->obedit != obact && (obact->flag & SELECT) && !(obact->id.lib)); + return (!ID_IS_LINKED_DATABLOCK(scene) && obact && scene->obedit != obact && + (obact->flag & SELECT) && !ID_IS_LINKED_DATABLOCK(obact)); } /* Helper for convert_exec */ -static Base *duplibase_for_convert(Scene *scene, Base *base, Object *ob) +static Base *duplibase_for_convert(Main *bmain, Scene *scene, Base *base, Object *ob) { Object *obn; Base *basen; @@ -1532,7 +1546,7 @@ static Base *duplibase_for_convert(Scene *scene, Base *base, Object *ob) ob = base->object; } - obn = BKE_object_copy(ob); + obn = BKE_object_copy(bmain, ob); DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); basen = MEM_mallocN(sizeof(Base), "duplibase"); @@ -1612,7 +1626,7 @@ static int convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(scene, base, NULL); + basen = duplibase_for_convert(bmain, scene, base, NULL); newob = basen->object; /* decrement original mesh's usage count */ @@ -1620,7 +1634,7 @@ static int convert_exec(bContext *C, wmOperator *op) id_us_min(&me->id); /* make a new copy of the mesh */ - newob->data = BKE_mesh_copy(me); + newob->data = BKE_mesh_copy(bmain, me); } else { newob = ob; @@ -1637,7 +1651,7 @@ static int convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(scene, base, NULL); + basen = duplibase_for_convert(bmain, scene, base, NULL); newob = basen->object; /* decrement original mesh's usage count */ @@ -1645,7 +1659,7 @@ static int convert_exec(bContext *C, wmOperator *op) id_us_min(&me->id); /* make a new copy of the mesh */ - newob->data = BKE_mesh_copy(me); + newob->data = BKE_mesh_copy(bmain, me); } else { newob = ob; @@ -1669,14 +1683,14 @@ static int convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(scene, base, NULL); + basen = duplibase_for_convert(bmain, scene, base, NULL); newob = basen->object; /* decrement original curve's usage count */ id_us_min(&((Curve *)newob->data)->id); /* make a new copy of the curve */ - newob->data = BKE_curve_copy(ob->data); + newob->data = BKE_curve_copy(bmain, ob->data); } else { newob = ob; @@ -1740,14 +1754,14 @@ static int convert_exec(bContext *C, wmOperator *op) if (target == OB_MESH) { if (keep_original) { - basen = duplibase_for_convert(scene, base, NULL); + basen = duplibase_for_convert(bmain, scene, base, NULL); newob = basen->object; /* decrement original curve's usage count */ id_us_min(&((Curve *)newob->data)->id); /* make a new copy of the curve */ - newob->data = BKE_curve_copy(ob->data); + newob->data = BKE_curve_copy(bmain, ob->data); } else { newob = ob; @@ -1775,7 +1789,7 @@ static int convert_exec(bContext *C, wmOperator *op) if (!(baseob->flag & OB_DONE)) { baseob->flag |= OB_DONE; - basen = duplibase_for_convert(scene, base, baseob); + basen = duplibase_for_convert(bmain, scene, base, baseob); newob = basen->object; mb = newob->data; @@ -1913,7 +1927,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base ; /* nothing? */ } else { - obn = BKE_object_copy(ob); + obn = BKE_object_copy(bmain, ob); DAG_id_tag_update(&obn->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); basen = MEM_mallocN(sizeof(Base), "duplibase"); @@ -1944,7 +1958,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (id) { ID_NEW_US(obn->mat[a]) else - obn->mat[a] = BKE_material_copy(obn->mat[a]); + obn->mat[a] = BKE_material_copy(bmain, obn->mat[a]); id_us_min(id); if (dupflag & USER_DUP_ACT) { @@ -1960,7 +1974,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (id) { ID_NEW_US(psys->part) else - psys->part = BKE_particlesettings_copy(psys->part); + psys->part = BKE_particlesettings_copy(bmain, psys->part); if (dupflag & USER_DUP_ACT) { BKE_animdata_copy_id_action(&psys->part->id); @@ -1979,7 +1993,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag & USER_DUP_MESH) { ID_NEW_US2(obn->data) else { - obn->data = BKE_mesh_copy(obn->data); + obn->data = BKE_mesh_copy(bmain, obn->data); didit = 1; } id_us_min(id); @@ -1989,7 +2003,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag & USER_DUP_CURVE) { ID_NEW_US2(obn->data) else { - obn->data = BKE_curve_copy(obn->data); + obn->data = BKE_curve_copy(bmain, obn->data); didit = 1; } id_us_min(id); @@ -1999,7 +2013,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag & USER_DUP_SURF) { ID_NEW_US2(obn->data) else { - obn->data = BKE_curve_copy(obn->data); + obn->data = BKE_curve_copy(bmain, obn->data); didit = 1; } id_us_min(id); @@ -2009,7 +2023,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag & USER_DUP_FONT) { ID_NEW_US2(obn->data) else { - obn->data = BKE_curve_copy(obn->data); + obn->data = BKE_curve_copy(bmain, obn->data); didit = 1; } id_us_min(id); @@ -2019,7 +2033,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag & USER_DUP_MBALL) { ID_NEW_US2(obn->data) else { - obn->data = BKE_mball_copy(obn->data); + obn->data = BKE_mball_copy(bmain, obn->data); didit = 1; } id_us_min(id); @@ -2029,7 +2043,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag & USER_DUP_LAMP) { ID_NEW_US2(obn->data) else { - obn->data = BKE_lamp_copy(obn->data); + obn->data = BKE_lamp_copy(bmain, obn->data); didit = 1; } id_us_min(id); @@ -2042,7 +2056,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag & USER_DUP_ARM) { ID_NEW_US2(obn->data) else { - obn->data = BKE_armature_copy(obn->data); + obn->data = BKE_armature_copy(bmain, obn->data); BKE_pose_rebuild(obn, obn->data); didit = 1; } @@ -2053,7 +2067,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag != 0) { ID_NEW_US2(obn->data) else { - obn->data = BKE_lattice_copy(obn->data); + obn->data = BKE_lattice_copy(bmain, obn->data); didit = 1; } id_us_min(id); @@ -2063,7 +2077,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag != 0) { ID_NEW_US2(obn->data) else { - obn->data = BKE_camera_copy(obn->data); + obn->data = BKE_camera_copy(bmain, obn->data); didit = 1; } id_us_min(id); @@ -2073,7 +2087,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag != 0) { ID_NEW_US2(obn->data) else { - obn->data = BKE_speaker_copy(obn->data); + obn->data = BKE_speaker_copy(bmain, obn->data); didit = 1; } id_us_min(id); @@ -2112,7 +2126,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (id) { ID_NEW_US((*matarar)[a]) else - (*matarar)[a] = BKE_material_copy((*matarar)[a]); + (*matarar)[a] = BKE_material_copy(bmain, (*matarar)[a]); id_us_min(id); } } @@ -2309,7 +2323,7 @@ static int join_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - if (!ob || ob->id.lib) return 0; + if (!ob || ID_IS_LINKED_DATABLOCK(ob)) return 0; if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE)) return ED_operator_screenactive(C); @@ -2362,7 +2376,7 @@ static int join_shapes_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - if (!ob || ob->id.lib) return 0; + if (!ob || ID_IS_LINKED_DATABLOCK(ob)) return 0; /* only meshes supported at the moment */ if (ob->type == OB_MESH) diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index d7b7fd7040e..db8a4c1960f 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -584,7 +584,7 @@ static int edit_constraint_poll_generic(bContext *C, StructRNA *rna_type) return 0; } - if (ob->id.lib || (ptr.id.data && ((ID *)ptr.id.data)->lib)) { + if (ID_IS_LINKED_DATABLOCK(ob) || (ptr.id.data && ID_IS_LINKED_DATABLOCK(ptr.id.data))) { CTX_wm_operator_poll_msg_set(C, "Cannot edit library data"); return 0; } diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index 47ee6752e51..acee69daab7 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -300,7 +300,7 @@ static void data_transfer_exec_preprocess_objects( } me = ob->data; - if (me->id.lib) { + if (ID_IS_LINKED_DATABLOCK(me)) { /* Do not transfer to linked data, not supported. */ BKE_reportf(op->reports, RPT_WARNING, "Skipping object '%s', linked data '%s' cannot be modified", ob->id.name + 2, me->id.name + 2); @@ -330,7 +330,7 @@ static bool data_transfer_exec_is_object_valid( me->id.tag &= ~LIB_TAG_DOIT; return true; } - else if (me->id.lib == NULL) { + else if (!ID_IS_LINKED_DATABLOCK(me)) { /* Do not transfer apply operation more than once. */ /* XXX This is not nice regarding vgroups, which are half-Object data... :/ */ BKE_reportf(op->reports, RPT_WARNING, @@ -387,7 +387,7 @@ static int data_transfer_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - if (reverse_transfer && ((ID *)(ob_src->data))->lib) { + if (reverse_transfer && ID_IS_LINKED_DATABLOCK(ob_src->data)) { /* Do not transfer to linked data, not supported. */ return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 01a567931b3..3dc7d8ebd4b 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -470,7 +470,7 @@ void ED_object_editmode_enter(bContext *C, int flag) View3D *v3d = NULL; bool ok = false; - if (scene->id.lib) return; + if (ID_IS_LINKED_DATABLOCK(scene)) return; if (sa && sa->spacetype == SPACE_VIEW3D) v3d = sa->spacedata.first; @@ -539,7 +539,7 @@ void ED_object_editmode_enter(bContext *C, int flag) * BKE_object_obdata_is_libdata that prevent the bugfix #6614, so * i add this little hack here. */ - if (arm->id.lib) { + if (ID_IS_LINKED_DATABLOCK(arm)) { error_libdata(); return; } @@ -621,7 +621,7 @@ static int editmode_toggle_poll(bContext *C) Object *ob = CTX_data_active_object(C); /* covers proxies too */ - if (ELEM(NULL, ob, ob->data) || ((ID *)ob->data)->lib) + if (ELEM(NULL, ob, ob->data) || ID_IS_LINKED_DATABLOCK(ob->data)) return 0; /* if hidden but in edit mode, we still display */ @@ -848,7 +848,7 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) Nurb *nu; bool do_depgraph_update = false; - if (scene->id.lib) return; + if (ID_IS_LINKED_DATABLOCK(scene)) return; if (!(ob = OBACT)) return; @@ -1436,7 +1436,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) { data = ob->data; - if (data && data->lib) { + if (data && ID_IS_LINKED_DATABLOCK(data)) { linked_data = true; continue; } @@ -1519,7 +1519,7 @@ static void UNUSED_FUNCTION(image_aspect) (Scene *scene, View3D *v3d) int a, b, done; if (scene->obedit) return; // XXX get from context - if (scene->id.lib) return; + if (ID_IS_LINKED_DATABLOCK(scene)) return; for (base = FIRSTBASE; base; base = base->next) { if (TESTBASELIB(v3d, base)) { diff --git a/source/blender/editors/object/object_group.c b/source/blender/editors/object/object_group.c index bcdd170c53c..0fe43c44d7d 100644 --- a/source/blender/editors/object/object_group.c +++ b/source/blender/editors/object/object_group.c @@ -440,7 +440,7 @@ static int group_link_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; /* Early return check, if the object is already in group - * we could sckip all the dependency check and just consider + * we could skip all the dependency check and just consider * operator is finished. */ if (BKE_group_object_exists(group, ob)) { diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 264945b00bf..b44ddf925a8 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -817,9 +817,9 @@ int edit_modifier_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type); Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C); - if (!ob || ob->id.lib) return 0; + if (!ob || ID_IS_LINKED_DATABLOCK(ob)) return 0; if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) return 0; - if (ptr.id.data && ((ID *)ptr.id.data)->lib) return 0; + if (ptr.id.data && ID_IS_LINKED_DATABLOCK(ptr.id.data)) return 0; return 1; } diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index bda4b6630fe..7c7e80edbf7 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -368,9 +368,21 @@ void ED_keymap_object(wmKeyConfig *keyconf) WM_keymap_verify_item(keymap, "OBJECT_OT_constraint_add_with_targets", CKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); WM_keymap_verify_item(keymap, "OBJECT_OT_constraints_clear", CKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); - WM_keymap_verify_item(keymap, "OBJECT_OT_location_clear", GKEY, KM_PRESS, KM_ALT, 0); - WM_keymap_verify_item(keymap, "OBJECT_OT_rotation_clear", RKEY, KM_PRESS, KM_ALT, 0); - WM_keymap_verify_item(keymap, "OBJECT_OT_scale_clear", SKEY, KM_PRESS, KM_ALT, 0); + + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_location_clear", GKEY, KM_PRESS, KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "clear_delta", false); + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_rotation_clear", RKEY, KM_PRESS, KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "clear_delta", false); + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_scale_clear", SKEY, KM_PRESS, KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "clear_delta", false); + + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_location_clear", GKEY, KM_PRESS, KM_ALT | KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "clear_delta", true); + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_rotation_clear", RKEY, KM_PRESS, KM_ALT | KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "clear_delta", true); + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_scale_clear", SKEY, KM_PRESS, KM_ALT | KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "clear_delta", true); + WM_keymap_verify_item(keymap, "OBJECT_OT_origin_clear", OKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "OBJECT_OT_hide_view_clear", HKEY, KM_PRESS, KM_ALT, 0); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 94d1a258063..ff2accf9d82 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -48,6 +48,7 @@ #include "DNA_world_types.h" #include "DNA_object_types.h" #include "DNA_vfont_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -291,17 +292,17 @@ static int make_proxy_invoke(bContext *C, wmOperator *op, const wmEvent *event) Object *ob = ED_object_active_context(C); /* sanity checks */ - if (!scene || scene->id.lib || !ob) + if (!scene || ID_IS_LINKED_DATABLOCK(scene) || !ob) return OPERATOR_CANCELLED; /* Get object to work on - use a menu if we need to... */ - if (ob->dup_group && ob->dup_group->id.lib) { + if (ob->dup_group && ID_IS_LINKED_DATABLOCK(ob->dup_group)) { /* gives menu with list of objects in group */ /* proxy_group_objects_menu(C, op, ob, ob->dup_group); */ WM_enum_search_invoke(C, op, event); return OPERATOR_CANCELLED; } - else if (ob->id.lib) { + else if (ID_IS_LINKED_DATABLOCK(ob)) { uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION); uiLayout *layout = UI_popup_menu_layout(pup); @@ -1466,7 +1467,7 @@ static int make_links_scene_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (scene_to->id.lib) { + if (ID_IS_LINKED_DATABLOCK(scene_to)) { BKE_report(op->reports, RPT_ERROR, "Cannot link objects into a linked scene"); return OPERATOR_CANCELLED; } @@ -1564,7 +1565,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op) ob_dst->data = obdata_id; /* if amount of material indices changed: */ - test_object_materials(bmain, ob_dst->data); + test_object_materials(ob_dst, ob_dst->data); DAG_id_tag_update(&ob_dst->id, OB_RECALC_DATA); break; @@ -1579,7 +1580,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op) case MAKE_LINKS_ANIMDATA: BKE_animdata_copy_id((ID *)ob_dst, (ID *)ob_src, false); if (ob_dst->data && ob_src->data) { - if (obdata_id->lib) { + if (ID_IS_LINKED_DATABLOCK(obdata_id)) { is_lib = true; break; } @@ -1621,7 +1622,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op) Curve *cu_src = ob_src->data; Curve *cu_dst = ob_dst->data; - if (obdata_id->lib) { + if (ID_IS_LINKED_DATABLOCK(obdata_id)) { is_lib = true; break; } @@ -1749,9 +1750,9 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in ob = base->object; if ((base->flag & flag) == flag) { - if (ob->id.lib == NULL && ob->id.us > 1) { + if (!ID_IS_LINKED_DATABLOCK(ob) && ob->id.us > 1) { /* base gets copy of object */ - obn = BKE_object_copy(ob); + obn = BKE_object_copy(bmain, ob); base->object = obn; if (copy_groups) { @@ -1762,6 +1763,14 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in else { /* copy already clears */ } + /* remap gpencil parenting */ + bGPdata *gpd = scene->gpd; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl->parent == ob) { + gpl->parent = obn; + } + } + base->flag = obn->flag; id_us_min(&ob->id); @@ -1784,7 +1793,7 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in } if (all_duplicated) { - groupn = BKE_group_copy(group); + groupn = BKE_group_copy(bmain, group); for (go = groupn->gobject.first; go; go = go->next) go->ob = (Object *)go->ob->id.newid; @@ -1821,21 +1830,21 @@ void ED_object_single_user(Main *bmain, Scene *scene, Object *ob) single_object_users(bmain, scene, NULL, OB_DONE, copy_groups); } -static void new_id_matar(Material **matar, const int totcol) +static void new_id_matar(Main *bmain, Material **matar, const int totcol) { ID *id; int a; for (a = 0; a < totcol; a++) { id = (ID *)matar[a]; - if (id && id->lib == NULL) { + if (id && !ID_IS_LINKED_DATABLOCK(id)) { if (id->newid) { matar[a] = (Material *)id->newid; id_us_plus(id->newid); id_us_min(id); } else if (id->us > 1) { - matar[a] = BKE_material_copy(matar[a]); + matar[a] = BKE_material_copy(bmain, matar[a]); id_us_min(id); id->newid = (ID *)matar[a]; } @@ -1857,15 +1866,15 @@ static void single_obdata_users(Main *bmain, Scene *scene, const int flag) for (base = FIRSTBASE; base; base = base->next) { ob = base->object; - if (ob->id.lib == NULL && (base->flag & flag) == flag) { + if (!ID_IS_LINKED_DATABLOCK(ob) && (base->flag & flag) == flag) { id = ob->data; - if (id && id->us > 1 && id->lib == NULL) { + if (id && id->us > 1 && !ID_IS_LINKED_DATABLOCK(id)) { DAG_id_tag_update(&ob->id, OB_RECALC_DATA); switch (ob->type) { case OB_LAMP: - ob->data = la = BKE_lamp_copy(ob->data); + ob->data = la = BKE_lamp_copy(bmain, ob->data); for (a = 0; a < MAX_MTEX; a++) { if (la->mtex[a]) { ID_NEW(la->mtex[a]->object); @@ -1873,37 +1882,37 @@ static void single_obdata_users(Main *bmain, Scene *scene, const int flag) } break; case OB_CAMERA: - ob->data = BKE_camera_copy(ob->data); + ob->data = BKE_camera_copy(bmain, ob->data); break; case OB_MESH: - ob->data = me = BKE_mesh_copy(ob->data); + ob->data = me = BKE_mesh_copy(bmain, ob->data); if (me->key) BKE_animdata_copy_id_action((ID *)me->key); break; case OB_MBALL: - ob->data = BKE_mball_copy(ob->data); + ob->data = BKE_mball_copy(bmain, ob->data); break; case OB_CURVE: case OB_SURF: case OB_FONT: - ob->data = cu = BKE_curve_copy(ob->data); + ob->data = cu = BKE_curve_copy(bmain, ob->data); ID_NEW(cu->bevobj); ID_NEW(cu->taperobj); if (cu->key) BKE_animdata_copy_id_action((ID *)cu->key); break; case OB_LATTICE: - ob->data = lat = BKE_lattice_copy(ob->data); + ob->data = lat = BKE_lattice_copy(bmain, ob->data); if (lat->key) BKE_animdata_copy_id_action((ID *)lat->key); break; case OB_ARMATURE: DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - ob->data = BKE_armature_copy(ob->data); + ob->data = BKE_armature_copy(bmain, ob->data); BKE_pose_rebuild(ob, ob->data); break; case OB_SPEAKER: - ob->data = BKE_speaker_copy(ob->data); + ob->data = BKE_speaker_copy(bmain, ob->data); break; default: if (G.debug & G_DEBUG) @@ -1938,14 +1947,14 @@ static void single_object_action_users(Scene *scene, const int flag) for (base = FIRSTBASE; base; base = base->next) { ob = base->object; - if (ob->id.lib == NULL && (flag == 0 || (base->flag & SELECT)) ) { + if (!ID_IS_LINKED_DATABLOCK(ob) && (flag == 0 || (base->flag & SELECT)) ) { DAG_id_tag_update(&ob->id, OB_RECALC_DATA); BKE_animdata_copy_id_action(&ob->id); } } } -static void single_mat_users(Scene *scene, const int flag, const bool do_textures) +static void single_mat_users(Main *bmain, Scene *scene, const int flag, const bool do_textures) { Object *ob; Base *base; @@ -1955,14 +1964,14 @@ static void single_mat_users(Scene *scene, const int flag, const bool do_texture for (base = FIRSTBASE; base; base = base->next) { ob = base->object; - if (ob->id.lib == NULL && (flag == 0 || (base->flag & SELECT)) ) { + if (!ID_IS_LINKED_DATABLOCK(ob) && (flag == 0 || (base->flag & SELECT)) ) { for (a = 1; a <= ob->totcol; a++) { ma = give_current_material(ob, a); if (ma) { /* do not test for LIB_TAG_NEW: this functions guaranteed delivers single_users! */ if (ma->id.us > 1) { - man = BKE_material_copy(ma); + man = BKE_material_copy(bmain, ma); BKE_animdata_copy_id_action(&man->id); man->id.us = 0; @@ -1973,7 +1982,7 @@ static void single_mat_users(Scene *scene, const int flag, const bool do_texture if (ma->mtex[b] && (tex = ma->mtex[b]->tex)) { if (tex->id.us > 1) { id_us_min(&tex->id); - tex = BKE_texture_copy(tex); + tex = BKE_texture_copy(bmain, tex); BKE_animdata_copy_id_action(&tex->id); man->mtex[b]->tex = tex; } @@ -1987,7 +1996,7 @@ static void single_mat_users(Scene *scene, const int flag, const bool do_texture } } -static void do_single_tex_user(Tex **from) +static void do_single_tex_user(Main *bmain, Tex **from) { Tex *tex, *texn; @@ -2000,7 +2009,7 @@ static void do_single_tex_user(Tex **from) id_us_min(&tex->id); } else if (tex->id.us > 1) { - texn = BKE_texture_copy(tex); + texn = BKE_texture_copy(bmain, tex); BKE_animdata_copy_id_action(&texn->id); tex->id.newid = (ID *)texn; id_us_min(&tex->id); @@ -2020,7 +2029,7 @@ static void single_tex_users_expand(Main *bmain) if (ma->id.tag & LIB_TAG_NEW) { for (b = 0; b < MAX_MTEX; b++) { if (ma->mtex[b] && ma->mtex[b]->tex) { - do_single_tex_user(&(ma->mtex[b]->tex)); + do_single_tex_user(bmain, &(ma->mtex[b]->tex)); } } } @@ -2030,7 +2039,7 @@ static void single_tex_users_expand(Main *bmain) if (la->id.tag & LIB_TAG_NEW) { for (b = 0; b < MAX_MTEX; b++) { if (la->mtex[b] && la->mtex[b]->tex) { - do_single_tex_user(&(la->mtex[b]->tex)); + do_single_tex_user(bmain, &(la->mtex[b]->tex)); } } } @@ -2040,7 +2049,7 @@ static void single_tex_users_expand(Main *bmain) if (wo->id.tag & LIB_TAG_NEW) { for (b = 0; b < MAX_MTEX; b++) { if (wo->mtex[b] && wo->mtex[b]->tex) { - do_single_tex_user(&(wo->mtex[b]->tex)); + do_single_tex_user(bmain, &(wo->mtex[b]->tex)); } } } @@ -2059,19 +2068,19 @@ static void single_mat_users_expand(Main *bmain) for (ob = bmain->object.first; ob; ob = ob->id.next) if (ob->id.tag & LIB_TAG_NEW) - new_id_matar(ob->mat, ob->totcol); + new_id_matar(bmain, ob->mat, ob->totcol); for (me = bmain->mesh.first; me; me = me->id.next) if (me->id.tag & LIB_TAG_NEW) - new_id_matar(me->mat, me->totcol); + new_id_matar(bmain, me->mat, me->totcol); for (cu = bmain->curve.first; cu; cu = cu->id.next) if (cu->id.tag & LIB_TAG_NEW) - new_id_matar(cu->mat, cu->totcol); + new_id_matar(bmain, cu->mat, cu->totcol); for (mb = bmain->mball.first; mb; mb = mb->id.next) if (mb->id.tag & LIB_TAG_NEW) - new_id_matar(mb->mat, mb->totcol); + new_id_matar(bmain, mb->mat, mb->totcol); /* material imats */ for (ma = bmain->mat.first; ma; ma = ma->id.next) @@ -2104,11 +2113,11 @@ static void make_local_makelocalmaterial(Material *ma) AnimData *adt; int b; - id_make_local(&ma->id, false); + id_make_local(G.main, &ma->id, false, false); for (b = 0; b < MAX_MTEX; b++) if (ma->mtex[b] && ma->mtex[b]->tex) - id_make_local(&ma->mtex[b]->tex->id, false); + id_make_local(G.main, &ma->mtex[b]->tex->id, false, false); adt = BKE_animdata_from_id(&ma->id); if (adt) BKE_animdata_make_local(adt); @@ -2184,7 +2193,7 @@ static bool make_local_all__instance_indirect_unused(Main *bmain, Scene *scene) bool changed = false; for (ob = bmain->object.first; ob; ob = ob->id.next) { - if (ob->id.lib && (ob->id.us == 0)) { + if (ID_IS_LINKED_DATABLOCK(ob) && (ob->id.us == 0)) { Base *base; id_us_plus(&ob->id); @@ -2237,7 +2246,7 @@ static int make_local_exec(bContext *C, wmOperator *op) } if (ob->id.lib) - id_make_local(&ob->id, false); + id_make_local(bmain, &ob->id, false, false); } CTX_DATA_END; @@ -2259,7 +2268,7 @@ static int make_local_exec(bContext *C, wmOperator *op) id = ob->data; if (id && (ELEM(mode, MAKE_LOCAL_SELECT_OBDATA, MAKE_LOCAL_SELECT_OBDATA_MATERIAL))) { - id_make_local(id, false); + id_make_local(bmain, id, false, false); adt = BKE_animdata_from_id(id); if (adt) BKE_animdata_make_local(adt); @@ -2275,7 +2284,7 @@ static int make_local_exec(bContext *C, wmOperator *op) } for (psys = ob->particlesystem.first; psys; psys = psys->next) - id_make_local(&psys->part->id, false); + id_make_local(bmain, &psys->part->id, false, false); adt = BKE_animdata_from_id(&ob->id); if (adt) BKE_animdata_make_local(adt); @@ -2294,7 +2303,7 @@ static int make_local_exec(bContext *C, wmOperator *op) for (b = 0; b < MAX_MTEX; b++) if (la->mtex[b] && la->mtex[b]->tex) - id_make_local(&la->mtex[b]->tex->id, false); + id_make_local(bmain, &la->mtex[b]->tex->id, false, false); } else { for (a = 0; a < ob->totcol; a++) { @@ -2376,7 +2385,7 @@ static int make_single_user_exec(bContext *C, wmOperator *op) } if (RNA_boolean_get(op->ptr, "material")) { - single_mat_users(scene, flag, RNA_boolean_get(op->ptr, "texture")); + single_mat_users(bmain, scene, flag, RNA_boolean_get(op->ptr, "texture")); } #if 0 /* can't do this separate from materials */ diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index 0cbbe46f461..f1b7186f8a1 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -577,7 +577,6 @@ static bool select_grouped_parent(bContext *C) /* Makes parent active and de-sel /* can be NULL if parent in other scene */ if (baspar && BASE_SELECTABLE(v3d, baspar)) { - ED_base_object_select(basact, BA_DESELECT); ED_base_object_select(baspar, BA_SELECT); ED_base_object_activate(C, baspar); changed = true; diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index 39bd34456be..e04114761e4 100644 --- a/source/blender/editors/object/object_shapekey.c +++ b/source/blender/editors/object/object_shapekey.c @@ -225,7 +225,7 @@ static int shape_key_mode_poll(bContext *C) { Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - return (ob && !ob->id.lib && data && !data->lib && ob->mode != OB_MODE_EDIT); + return (ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data) && ob->mode != OB_MODE_EDIT); } static int shape_key_mode_exists_poll(bContext *C) @@ -234,7 +234,7 @@ static int shape_key_mode_exists_poll(bContext *C) ID *data = (ob) ? ob->data : NULL; /* same as shape_key_mode_poll */ - return (ob && !ob->id.lib && data && !data->lib && ob->mode != OB_MODE_EDIT) && + return (ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data) && ob->mode != OB_MODE_EDIT) && /* check a keyblock exists */ (BKE_keyblock_from_object(ob) != NULL); } @@ -246,14 +246,15 @@ static int shape_key_move_poll(bContext *C) ID *data = (ob) ? ob->data : NULL; Key *key = BKE_key_from_object(ob); - return (ob && !ob->id.lib && data && !data->lib && ob->mode != OB_MODE_EDIT && key && key->totkey > 1); + return (ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data) && + ob->mode != OB_MODE_EDIT && key && key->totkey > 1); } static int shape_key_poll(bContext *C) { Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - return (ob && !ob->id.lib && data && !data->lib); + return (ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data)); } static int shape_key_add_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index d2bbb73b597..4d7d7df0d2f 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -76,60 +76,87 @@ /*************************** Clear Transformation ****************************/ /* clear location of object */ -static void object_clear_loc(Object *ob) +static void object_clear_loc(Object *ob, const bool clear_delta) { /* clear location if not locked */ - if ((ob->protectflag & OB_LOCK_LOCX) == 0) - ob->loc[0] = ob->dloc[0] = 0.0f; - if ((ob->protectflag & OB_LOCK_LOCY) == 0) - ob->loc[1] = ob->dloc[1] = 0.0f; - if ((ob->protectflag & OB_LOCK_LOCZ) == 0) - ob->loc[2] = ob->dloc[2] = 0.0f; + if ((ob->protectflag & OB_LOCK_LOCX) == 0) { + ob->loc[0] = 0.0f; + if (clear_delta) ob->dloc[0] = 0.0f; + } + if ((ob->protectflag & OB_LOCK_LOCY) == 0) { + ob->loc[1] = 0.0f; + if (clear_delta) ob->dloc[1] = 0.0f; + } + if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { + ob->loc[2] = 0.0f; + if (clear_delta) ob->dloc[2] = 0.0f; + } } /* clear rotation of object */ -static void object_clear_rot(Object *ob) +static void object_clear_rot(Object *ob, const bool clear_delta) { /* clear rotations that aren't locked */ if (ob->protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) { if (ob->protectflag & OB_LOCK_ROT4D) { /* perform clamping on a component by component basis */ if (ob->rotmode == ROT_MODE_AXISANGLE) { - if ((ob->protectflag & OB_LOCK_ROTW) == 0) - ob->rotAngle = ob->drotAngle = 0.0f; - if ((ob->protectflag & OB_LOCK_ROTX) == 0) - ob->rotAxis[0] = ob->drotAxis[0] = 0.0f; - if ((ob->protectflag & OB_LOCK_ROTY) == 0) - ob->rotAxis[1] = ob->drotAxis[1] = 0.0f; - if ((ob->protectflag & OB_LOCK_ROTZ) == 0) - ob->rotAxis[2] = ob->drotAxis[2] = 0.0f; + if ((ob->protectflag & OB_LOCK_ROTW) == 0) { + ob->rotAngle = 0.0f; + if (clear_delta) ob->drotAngle = 0.0f; + } + if ((ob->protectflag & OB_LOCK_ROTX) == 0) { + ob->rotAxis[0] = 0.0f; + if (clear_delta) ob->drotAxis[0] = 0.0f; + } + if ((ob->protectflag & OB_LOCK_ROTY) == 0) { + ob->rotAxis[1] = 0.0f; + if (clear_delta) ob->drotAxis[1] = 0.0f; + } + if ((ob->protectflag & OB_LOCK_ROTZ) == 0) { + ob->rotAxis[2] = 0.0f; + if (clear_delta) ob->drotAxis[2] = 0.0f; + } /* check validity of axis - axis should never be 0,0,0 (if so, then we make it rotate about y) */ if (IS_EQF(ob->rotAxis[0], ob->rotAxis[1]) && IS_EQF(ob->rotAxis[1], ob->rotAxis[2])) ob->rotAxis[1] = 1.0f; - if (IS_EQF(ob->drotAxis[0], ob->drotAxis[1]) && IS_EQF(ob->drotAxis[1], ob->drotAxis[2])) + if (IS_EQF(ob->drotAxis[0], ob->drotAxis[1]) && IS_EQF(ob->drotAxis[1], ob->drotAxis[2]) && clear_delta) ob->drotAxis[1] = 1.0f; } else if (ob->rotmode == ROT_MODE_QUAT) { - if ((ob->protectflag & OB_LOCK_ROTW) == 0) - ob->quat[0] = ob->dquat[0] = 1.0f; - if ((ob->protectflag & OB_LOCK_ROTX) == 0) - ob->quat[1] = ob->dquat[1] = 0.0f; - if ((ob->protectflag & OB_LOCK_ROTY) == 0) - ob->quat[2] = ob->dquat[2] = 0.0f; - if ((ob->protectflag & OB_LOCK_ROTZ) == 0) - ob->quat[3] = ob->dquat[3] = 0.0f; - + if ((ob->protectflag & OB_LOCK_ROTW) == 0) { + ob->quat[0] = 1.0f; + if (clear_delta) ob->dquat[0] = 1.0f; + } + if ((ob->protectflag & OB_LOCK_ROTX) == 0) { + ob->quat[1] = 0.0f; + if (clear_delta) ob->dquat[1] = 0.0f; + } + if ((ob->protectflag & OB_LOCK_ROTY) == 0) { + ob->quat[2] = 0.0f; + if (clear_delta) ob->dquat[2] = 0.0f; + } + if ((ob->protectflag & OB_LOCK_ROTZ) == 0) { + ob->quat[3] = 0.0f; + if (clear_delta) ob->dquat[3] = 0.0f; + } /* TODO: does this quat need normalizing now? */ } else { /* the flag may have been set for the other modes, so just ignore the extra flag... */ - if ((ob->protectflag & OB_LOCK_ROTX) == 0) - ob->rot[0] = ob->drot[0] = 0.0f; - if ((ob->protectflag & OB_LOCK_ROTY) == 0) - ob->rot[1] = ob->drot[1] = 0.0f; - if ((ob->protectflag & OB_LOCK_ROTZ) == 0) - ob->rot[2] = ob->drot[2] = 0.0f; + if ((ob->protectflag & OB_LOCK_ROTX) == 0) { + ob->rot[0] = 0.0f; + if (clear_delta) ob->drot[0] = 0.0f; + } + if ((ob->protectflag & OB_LOCK_ROTY) == 0) { + ob->rot[1] = 0.0f; + if (clear_delta) ob->drot[1] = 0.0f; + } + if ((ob->protectflag & OB_LOCK_ROTZ) == 0) { + ob->rot[2] = 0.0f; + if (clear_delta) ob->drot[2] = 0.0f; + } } } else { @@ -175,34 +202,34 @@ static void object_clear_rot(Object *ob) else { if (ob->rotmode == ROT_MODE_QUAT) { unit_qt(ob->quat); - unit_qt(ob->dquat); + if (clear_delta) unit_qt(ob->dquat); } else if (ob->rotmode == ROT_MODE_AXISANGLE) { unit_axis_angle(ob->rotAxis, &ob->rotAngle); - unit_axis_angle(ob->drotAxis, &ob->drotAngle); + if (clear_delta) unit_axis_angle(ob->drotAxis, &ob->drotAngle); } else { zero_v3(ob->rot); - zero_v3(ob->drot); + if (clear_delta) zero_v3(ob->drot); } } } /* clear scale of object */ -static void object_clear_scale(Object *ob) +static void object_clear_scale(Object *ob, const bool clear_delta) { /* clear scale factors which are not locked */ if ((ob->protectflag & OB_LOCK_SCALEX) == 0) { - ob->dscale[0] = 1.0f; ob->size[0] = 1.0f; + if (clear_delta) ob->dscale[0] = 1.0f; } if ((ob->protectflag & OB_LOCK_SCALEY) == 0) { - ob->dscale[1] = 1.0f; ob->size[1] = 1.0f; + if (clear_delta) ob->dscale[1] = 1.0f; } if ((ob->protectflag & OB_LOCK_SCALEZ) == 0) { - ob->dscale[2] = 1.0f; ob->size[2] = 1.0f; + if (clear_delta) ob->dscale[2] = 1.0f; } } @@ -210,10 +237,12 @@ static void object_clear_scale(Object *ob) /* generic exec for clear-transform operators */ static int object_clear_transform_generic_exec(bContext *C, wmOperator *op, - void (*clear_func)(Object *), const char default_ksName[]) + void (*clear_func)(Object *, const bool), + const char default_ksName[]) { Scene *scene = CTX_data_scene(C); KeyingSet *ks; + const bool clear_delta = RNA_boolean_get(op->ptr, "clear_delta"); /* sanity checks */ if (ELEM(NULL, clear_func, default_ksName)) { @@ -231,10 +260,10 @@ static int object_clear_transform_generic_exec(bContext *C, wmOperator *op, { if (!(ob->mode & OB_MODE_WEIGHT_PAINT)) { /* run provided clearing function */ - clear_func(ob); - + clear_func(ob, clear_delta); + ED_autokeyframe_object(C, scene, ob, ks); - + /* tag for updates */ DAG_id_tag_update(&ob->id, OB_RECALC_OB); } @@ -268,6 +297,11 @@ void OBJECT_OT_location_clear(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + + /* properties */ + ot->prop = RNA_def_boolean(ot->srna, "clear_delta", false, "Clear Delta", + "Clear delta location in addition to clearing the normal location transform"); } static int object_rotation_clear_exec(bContext *C, wmOperator *op) @@ -288,6 +322,10 @@ void OBJECT_OT_rotation_clear(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_boolean(ot->srna, "clear_delta", false, "Clear Delta", + "Clear delta rotation in addition to clearing the normal rotation transform"); } static int object_scale_clear_exec(bContext *C, wmOperator *op) @@ -308,6 +346,10 @@ void OBJECT_OT_scale_clear(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_boolean(ot->srna, "clear_delta", false, "Clear Delta", + "Clear delta scale in addition to clearing the normal scale transform"); } /* --------------- */ @@ -391,7 +433,7 @@ static int apply_objects_internal(bContext *C, ReportList *reports, bool apply_l changed = false; } - if (obdata->lib) { + if (ID_IS_LINKED_DATABLOCK(obdata)) { BKE_reportf(reports, RPT_ERROR, "Cannot apply to library data: Object \"%s\", %s \"%s\", aborting", ob->id.name + 2, BKE_idcode_to_name(GS(obdata->name)), obdata->name + 2); @@ -792,7 +834,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) if (ob->data == NULL) { /* special support for dupligroups */ if ((ob->transflag & OB_DUPLIGROUP) && ob->dup_group && (ob->dup_group->id.tag & LIB_TAG_DOIT) == 0) { - if (ob->dup_group->id.lib) { + if (ID_IS_LINKED_DATABLOCK(ob->dup_group)) { tot_lib_error++; } else { @@ -817,7 +859,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } } } - else if (((ID *)ob->data)->lib) { + else if (ID_IS_LINKED_DATABLOCK(ob->data)) { tot_lib_error++; } diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 414cc476be5..ad41fb23a69 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -2447,8 +2447,8 @@ static int vertex_group_poll(bContext *C) Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - return (ob && !ob->id.lib && - data && !data->lib && + return (ob && !ID_IS_LINKED_DATABLOCK(ob) && + data && !ID_IS_LINKED_DATABLOCK(data) && OB_TYPE_SUPPORT_VGROUP(ob->type) && ob->defbase.first); } @@ -2457,7 +2457,8 @@ static int vertex_group_supported_poll(bContext *C) { Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - return (ob && !ob->id.lib && OB_TYPE_SUPPORT_VGROUP(ob->type) && data && !data->lib); + return (ob && !ID_IS_LINKED_DATABLOCK(ob) && OB_TYPE_SUPPORT_VGROUP(ob->type) && + data && !ID_IS_LINKED_DATABLOCK(data)); } static int vertex_group_mesh_poll(bContext *C) @@ -2465,8 +2466,8 @@ static int vertex_group_mesh_poll(bContext *C) Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - return (ob && !ob->id.lib && - data && !data->lib && + return (ob && !ID_IS_LINKED_DATABLOCK(ob) && + data && !ID_IS_LINKED_DATABLOCK(data) && ob->type == OB_MESH && ob->defbase.first); } @@ -2475,7 +2476,7 @@ static int UNUSED_FUNCTION(vertex_group_mesh_supported_poll)(bContext *C) { Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib); + return (ob && !ID_IS_LINKED_DATABLOCK(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED_DATABLOCK(data)); } @@ -2484,7 +2485,7 @@ static int UNUSED_FUNCTION(vertex_group_poll_edit) (bContext *C) Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - if (!(ob && !ob->id.lib && data && !data->lib)) + if (!(ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data))) return 0; return BKE_object_is_in_editmode_vgroup(ob); @@ -2496,7 +2497,7 @@ static int vertex_group_vert_select_poll_ex(bContext *C, const short ob_type_fla Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - if (!(ob && !ob->id.lib && data && !data->lib)) + if (!(ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data))) return false; if (ob_type_flag && (((1 << ob->type) & ob_type_flag)) == 0) { @@ -2536,7 +2537,7 @@ static int vertex_group_vert_select_unlocked_poll(bContext *C) Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - if (!(ob && !ob->id.lib && data && !data->lib)) + if (!(ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data))) return 0; if (!(BKE_object_is_in_editmode_vgroup(ob) || @@ -2559,7 +2560,7 @@ static int vertex_group_vert_select_mesh_poll(bContext *C) Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; - if (!(ob && !ob->id.lib && data && !data->lib)) + if (!(ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data))) return 0; /* only difference to #vertex_group_vert_select_poll */ @@ -2748,7 +2749,7 @@ static int vertex_group_select_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); - if (!ob || ob->id.lib) + if (!ob || ID_IS_LINKED_DATABLOCK(ob)) return OPERATOR_CANCELLED; vgroup_select_verts(ob, 1); @@ -3537,8 +3538,10 @@ static int vgroup_sort_name(const void *def_a_ptr, const void *def_b_ptr) return BLI_natstrcmp(def_a->name, def_b->name); } -/* Sorts the weight groups according to the bone hierarchy of the - associated armature (similar to how bones are ordered in the Outliner) */ +/** + * Sorts the weight groups according to the bone hierarchy of the + * associated armature (similar to how bones are ordered in the Outliner) + */ static void vgroup_sort_bone_hierarchy(Object *ob, ListBase *bonebase) { if (bonebase == NULL) { diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 30c102b70c5..e22a145b3a6 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -4693,7 +4693,7 @@ static int particle_edit_toggle_poll(bContext *C) if (ob == NULL || ob->type != OB_MESH) return 0; - if (!ob->data || ((ID *)ob->data)->lib) + if (!ob->data || ID_IS_LINKED_DATABLOCK(ob->data)) return 0; if (CTX_data_edit_object(C)) return 0; diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 1297133e1a2..0dd00df6182 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -195,7 +195,7 @@ static int new_particle_settings_exec(bContext *C, wmOperator *UNUSED(op)) /* add or copy particle setting */ if (psys->part) - part= BKE_particlesettings_copy(psys->part); + part= BKE_particlesettings_copy(bmain, psys->part); else part= psys_new_settings("ParticleSettings", bmain); @@ -974,7 +974,7 @@ static void remove_particle_systems_from_object(Object *ob_to) if (ob_to->type != OB_MESH) return; - if (!ob_to->data || ((ID *)ob_to->data)->lib) + if (!ob_to->data || ID_IS_LINKED_DATABLOCK(ob_to->data)) return; for (md = ob_to->modifiers.first; md; md = md_next) { @@ -1004,7 +1004,7 @@ static bool copy_particle_systems_to_object(Scene *scene, Object *ob_from, Parti if (ob_to->type != OB_MESH) return false; - if (!ob_to->data || ((ID *)ob_to->data)->lib) + if (!ob_to->data || ID_IS_LINKED_DATABLOCK(ob_to->data)) return false; /* For remapping we need a valid DM. diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 1203889cf0e..8c5d25ad44d 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -1482,7 +1482,7 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C) void render_view3d_update(RenderEngine *engine, const bContext *C) { /* this shouldn't be needed and causes too many database rebuilds, but we - * aren't actually tracking updates for all relevent datablocks so this is + * aren't actually tracking updates for all relevant datablocks so this is * a catch-all for updates */ engine->update_flag |= RE_ENGINE_UPDATE_DATABASE; diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 06291487809..4477c491dd9 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -43,6 +43,7 @@ #include "DNA_scene_types.h" #include "DNA_object_types.h" +#include "DNA_gpencil_types.h" #include "BKE_camera.h" #include "BKE_context.h" @@ -414,6 +415,99 @@ static void screen_opengl_render_write(OGLRender *oglrender) else printf("OpenGL Render failed to write '%s'\n", name); } +static void addAlphaOverFloat(float dest[4], const float source[4]) +{ + /* d = s + (1-alpha_s)d*/ + float mul; + + mul = 1.0f - source[3]; + + dest[0] = (mul * dest[0]) + source[0]; + dest[1] = (mul * dest[1]) + source[1]; + dest[2] = (mul * dest[2]) + source[2]; + dest[3] = (mul * dest[3]) + source[3]; + +} + +/* add renderlayer and renderpass for each grease pencil layer for using in composition */ +static void add_gpencil_renderpass(OGLRender *oglrender, RenderResult *rr, RenderView *rv) +{ + bGPdata *gpd = oglrender->scene->gpd; + Scene *scene = oglrender->scene; + + /* sanity checks */ + if (gpd == NULL) { + return; + } + if (scene == NULL) { + return; + } + if (BLI_listbase_is_empty(&gpd->layers)) { + return; + } + + /* save old alpha mode */ + short oldalphamode = scene->r.alphamode; + /* set alpha transparent for gp */ + scene->r.alphamode = R_ALPHAPREMUL; + + /* saves layer status */ + short *oldsts = MEM_mallocN(BLI_listbase_count(&gpd->layers) * sizeof(short), "temp_gplayers_flag"); + int i = 0; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + oldsts[i] = gpl->flag; + ++i; + } + /* loop all layers to create separate render */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* dont draw layer if hidden */ + if (gpl->flag & GP_LAYER_HIDE) + continue; + /* hide all layer except current */ + for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) { + if (gpl != gph) { + gph->flag |= GP_LAYER_HIDE; + } + } + + /* render this gp layer */ + screen_opengl_render_doit(oglrender, rr); + + /* add RendePass composite */ + RenderPass *rp = RE_create_gp_pass(rr, gpl->info, rv->name); + + /* copy image data from rectf */ + float *src = RE_RenderViewGetById(rr, oglrender->view_id)->rectf; + float *dest = rp->rect; + + float *pixSrc, *pixDest; + int x, y, rectx, recty; + rectx = rr->rectx; + recty = rr->recty; + for (y = 0; y < recty; y++) { + for (x = 0; x < rectx; x++) { + pixSrc = src + 4 * (rectx * y + x); + if (pixSrc[3] > 0.0) { + pixDest = dest + 4 * (rectx * y + x); + addAlphaOverFloat(pixDest, pixSrc); + } + } + } + + /* back layer status */ + i = 0; + for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) { + gph->flag = oldsts[i]; + ++i; + } + } + /* free memory */ + MEM_freeN(oldsts); + + /* back default alpha mode */ + scene->r.alphamode = oldalphamode; +} + static void screen_opengl_render_apply(OGLRender *oglrender) { RenderResult *rr; @@ -449,6 +543,9 @@ static void screen_opengl_render_apply(OGLRender *oglrender) BLI_assert(view_id < oglrender->views_len); RE_SetActiveRenderView(oglrender->re, rv->name); oglrender->view_id = view_id; + /* add grease pencil passes */ + add_gpencil_renderpass(oglrender, rr, rv); + /* render composite */ screen_opengl_render_doit(oglrender, rr); } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 132c3fa5438..b4f3426677a 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -864,7 +864,7 @@ static void shader_preview_free(void *customdata) /* get rid of copied world */ BLI_remlink(&pr_main->world, sp->worldcopy); /* T32865 - we need to unlink the texture copies, unlike for materials */ - BKE_libblock_relink_ex(sp->worldcopy, NULL, NULL, true); + BKE_libblock_relink_ex(pr_main, sp->worldcopy, NULL, NULL, true); BKE_world_free(sp->worldcopy); properties = IDP_GetProperties((ID *)sp->worldcopy, false); @@ -881,7 +881,7 @@ static void shader_preview_free(void *customdata) /* get rid of copied lamp */ BLI_remlink(&pr_main->lamp, sp->lampcopy); - BKE_libblock_relink_ex(sp->lampcopy, NULL, NULL, true); + BKE_libblock_relink_ex(pr_main, sp->lampcopy, NULL, NULL, true); BKE_lamp_free(sp->lampcopy); properties = IDP_GetProperties((ID *)sp->lampcopy, false); @@ -1210,7 +1210,7 @@ void ED_preview_icon_job(const bContext *C, void *owner, ID *id, unsigned int *r /* setup job */ WM_jobs_customdata_set(wm_job, ip, icon_preview_free); - WM_jobs_timer(wm_job, 0.1, NC_MATERIAL, NC_MATERIAL); + WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW); WM_jobs_callbacks(wm_job, icon_preview_startjob_all_sizes, NULL, NULL, icon_preview_endjob); WM_jobs_start(CTX_wm_manager(C), wm_job); diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 20e3c40360f..49cb8a8730a 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -105,7 +105,7 @@ static int material_slot_add_exec(bContext *C, wmOperator *UNUSED(op)) if (!ob) return OPERATOR_CANCELLED; - object_add_material_slot(ob); + BKE_object_material_slot_add(ob); if (ob->mode & OB_MODE_TEXTURE_PAINT) { Scene *scene = CTX_data_scene(C); @@ -148,7 +148,7 @@ static int material_slot_remove_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - object_remove_material_slot(ob); + BKE_object_material_slot_remove(ob); if (ob->mode & OB_MODE_TEXTURE_PAINT) { Scene *scene = CTX_data_scene(C); @@ -467,7 +467,7 @@ static int new_material_exec(bContext *C, wmOperator *UNUSED(op)) /* add or copy material */ if (ma) { - ma = BKE_material_copy(ma); + ma = BKE_material_copy(bmain, ma); } else { ma = BKE_material_add(bmain, DATA_("Material")); @@ -521,7 +521,7 @@ static int new_texture_exec(bContext *C, wmOperator *UNUSED(op)) /* add or copy texture */ if (tex) { - tex = BKE_texture_copy(tex); + tex = BKE_texture_copy(bmain, tex); } else { tex = BKE_texture_add(bmain, DATA_("Texture")); @@ -532,7 +532,7 @@ static int new_texture_exec(bContext *C, wmOperator *UNUSED(op)) if (prop) { /* when creating new ID blocks, use is already 1, but RNA - * pointer se also increases user, so this compensates it */ + * pointer use also increases user, so this compensates it */ id_us_min(&tex->id); if (ptr.id.data && GS(((ID *)ptr.id.data)->name) == ID_MA && @@ -579,7 +579,7 @@ static int new_world_exec(bContext *C, wmOperator *UNUSED(op)) /* add or copy world */ if (wo) { - wo = BKE_world_copy(wo); + wo = BKE_world_copy(bmain, wo); } else { wo = add_world(bmain, DATA_("World")); @@ -595,7 +595,7 @@ static int new_world_exec(bContext *C, wmOperator *UNUSED(op)) if (prop) { /* when creating new ID blocks, use is already 1, but RNA - * pointer se also increases user, so this compensates it */ + * pointer use also increases user, so this compensates it */ id_us_min(&wo->id); RNA_id_pointer_create(&wo->id, &idptr); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index ab8b7d4e138..8d058ed2081 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -580,15 +580,20 @@ void ED_region_tag_refresh_ui(ARegion *ar) void ED_region_tag_redraw_partial(ARegion *ar, const rcti *rct) { if (ar && !(ar->do_draw & RGN_DRAWING)) { - if (!(ar->do_draw & RGN_DRAW)) { + if (!(ar->do_draw & (RGN_DRAW | RGN_DRAW_PARTIAL))) { /* no redraw set yet, set partial region */ ar->do_draw |= RGN_DRAW_PARTIAL; ar->drawrct = *rct; } else if (ar->drawrct.xmin != ar->drawrct.xmax) { + BLI_assert((ar->do_draw & RGN_DRAW_PARTIAL) != 0); /* partial redraw already set, expand region */ BLI_rcti_union(&ar->drawrct, rct); } + else { + BLI_assert((ar->do_draw & RGN_DRAW) != 0); + /* Else, full redraw is already requested, nothing to do here. */ + } } } diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index f61ad348501..22d95d77d55 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -85,7 +85,8 @@ const char *screen_context_dir[] = { "sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */ "gpencil_data", "gpencil_data_owner", /* grease pencil data */ "visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes", - "active_gpencil_layer", "active_gpencil_frame", + "active_gpencil_layer", "active_gpencil_frame", "active_gpencil_palette", + "active_gpencil_palettecolor", "active_gpencil_brush", "active_operator", NULL}; @@ -474,6 +475,44 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } } + else if (CTX_data_equals(member, "active_gpencil_palette")) { + /* XXX: see comment for gpencil_data case... */ + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + + if (gpd) { + bGPDpalette *palette = gpencil_palette_getactive(gpd); + + if (palette) { + CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPalette, palette); + return 1; + } + } + } + else if (CTX_data_equals(member, "active_gpencil_palettecolor")) { + /* XXX: see comment for gpencil_data case... */ + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + + if (gpd) { + bGPDpalette *palette = gpencil_palette_getactive(gpd); + + if (palette) { + bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette); + if (palcolor) { + CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPaletteColor, palcolor); + return 1; + } + } + } + } + else if (CTX_data_equals(member, "active_gpencil_brush")) { + /* XXX: see comment for gpencil_data case... */ + bGPDbrush *brush = gpencil_brush_getactive(scene->toolsettings); + + if (brush) { + CTX_data_pointer_set(result, NULL, &RNA_GPencilBrush, brush); + return 1; + } + } else if (CTX_data_equals(member, "active_gpencil_frame")) { /* XXX: see comment for gpencil_data case... */ bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); @@ -533,6 +572,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult for (gps = gpf->strokes.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use_direct(sa, gps)) { + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps); } } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 62aeca4b9d1..677a6472c72 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -1943,6 +1943,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s sc = ED_screen_add(win, oldscreen->scene, newname); sc->state = state; sc->redraws_flag = oldscreen->redraws_flag; + sc->temp = oldscreen->temp; /* timer */ sc->animtimer = oldscreen->animtimer; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 0c0a6c93b3e..6dbb5db53d0 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -168,7 +168,7 @@ int ED_operator_screen_mainwinactive(bContext *C) int ED_operator_scene_editable(bContext *C) { Scene *scene = CTX_data_scene(C); - if (scene && scene->id.lib == NULL) + if (scene && !ID_IS_LINKED_DATABLOCK(scene)) return 1; return 0; } @@ -178,7 +178,7 @@ int ED_operator_objectmode(bContext *C) Scene *scene = CTX_data_scene(C); Object *obact = CTX_data_active_object(C); - if (scene == NULL || scene->id.lib) + if (scene == NULL || ID_IS_LINKED_DATABLOCK(scene)) return 0; if (CTX_data_edit_object(C)) return 0; @@ -279,7 +279,7 @@ int ED_operator_node_editable(bContext *C) { SpaceNode *snode = CTX_wm_space_node(C); - if (snode && snode->edittree && snode->edittree->id.lib == NULL) + if (snode && snode->edittree && !ID_IS_LINKED_DATABLOCK(snode->edittree)) return 1; return 0; @@ -341,20 +341,20 @@ int ED_operator_object_active(bContext *C) int ED_operator_object_active_editable(bContext *C) { Object *ob = ED_object_active_context(C); - return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob)); + return ((ob != NULL) && !ID_IS_LINKED_DATABLOCK(ob) && !ed_object_hidden(ob)); } int ED_operator_object_active_editable_mesh(bContext *C) { Object *ob = ED_object_active_context(C); - return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob) && - (ob->type == OB_MESH) && !(((ID *)ob->data)->lib)); + return ((ob != NULL) && !ID_IS_LINKED_DATABLOCK(ob) && !ed_object_hidden(ob) && + (ob->type == OB_MESH) && !ID_IS_LINKED_DATABLOCK(ob->data)); } int ED_operator_object_active_editable_font(bContext *C) { Object *ob = ED_object_active_context(C); - return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob) && + return ((ob != NULL) && !ID_IS_LINKED_DATABLOCK(ob) && !ed_object_hidden(ob) && (ob->type == OB_FONT)); } @@ -3955,7 +3955,7 @@ static int scene_new_exec(bContext *C, wmOperator *op) newscene = BKE_scene_add(bmain, DATA_("Scene")); } else { /* different kinds of copying */ - newscene = BKE_scene_copy(scene, type); + newscene = BKE_scene_copy(bmain, scene, type); /* these can't be handled in blenkernel currently, so do them here */ if (type == SCE_COPY_LINK_DATA) { diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index eba9448aa40..53c11e2a6a9 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1016,7 +1016,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) translation[1] = y; outline_alpha = 0.5; outline_col = brush->add_col; - final_radius = (BKE_brush_size_get(scene, brush) * zoomx) / U.pixelsize; + final_radius = (BKE_brush_size_get(scene, brush) * zoomx); /* don't calculate rake angles while a stroke is active because the rake variables are global and * we may get interference with the stroke itself. For line strokes, such interference is visible */ diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 941f19ca0f0..0924c9a9288 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -1342,7 +1342,7 @@ static int texture_paint_toggle_poll(bContext *C) Object *ob = CTX_data_active_object(C); if (ob == NULL || ob->type != OB_MESH) return 0; - if (!ob->data || ((ID *)ob->data)->lib) + if (!ob->data || ID_IS_LINKED_DATABLOCK(ob->data)) return 0; if (CTX_data_edit_object(C)) return 0; diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 080bd5b73c7..9474a46d716 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -1126,8 +1126,8 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign /* blend into canvas */ for (a = 0; a < tot; a++) { ED_imapaint_dirty_region(s->image, s->canvas, - region[a].destx, region[a].desty, - region[a].width, region[a].height, true); + region[a].destx, region[a].desty, + region[a].width, region[a].height, true); if (s->do_masking) { /* masking, find original pixels tiles from undo buffer to composite over */ diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 6ed969cb270..d60c8e8dbd9 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -183,8 +183,8 @@ BLI_INLINE unsigned char f_to_char(const float val) /* to avoid locking in tile initialization */ #define TILE_PENDING SET_INT_IN_POINTER(-1) -/* This is mainly a convenience struct used so we can keep an array of images we use - * Thir imbufs, etc, in 1 array, When using threads this array is copied for each thread +/* This is mainly a convenience struct used so we can keep an array of images we use - + * their imbufs, etc, in 1 array, When using threads this array is copied for each thread * because 'partRedrawRect' and 'touch' values would not be thread safe */ typedef struct ProjPaintImage { Image *ima; @@ -202,7 +202,7 @@ typedef struct ProjPaintImage { */ typedef struct ProjStrokeHandle { /* Support for painting from multiple views at once, - * currently used to impliment symmetry painting, + * currently used to implement symmetry painting, * we can assume at least the first is set while painting. */ struct ProjPaintState *ps_views[8]; int ps_views_tot; @@ -2174,7 +2174,7 @@ static void project_bucket_clip_face( if ((*tot) < 3) { /* no intersections to speak of, but more probable is that all face is just outside the - * rectangle and culled due to float precision issues. Since above teste have failed, + * rectangle and culled due to float precision issues. Since above tests have failed, * just dump triangle as is for painting */ *tot = 0; copy_v2_v2(bucket_bounds_uv[*tot], uv1co); (*tot)++; diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index bf923415f01..f88b64129e7 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -69,7 +69,7 @@ static int brush_add_exec(bContext *C, wmOperator *UNUSED(op)) PaintMode mode = BKE_paintmode_get_active_from_context(C); if (br) - br = BKE_brush_copy(br); + br = BKE_brush_copy(bmain, br); else br = BKE_brush_add(bmain, "Brush", BKE_paint_object_mode_from_paint_mode(mode)); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index aa17cb02fe5..1431958501d 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -491,31 +491,30 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float } /* Returns zero if no sculpt changes should be made, non-zero otherwise */ -static int paint_smooth_stroke(PaintStroke *stroke, float output[2], float *outpressure, - const PaintSample *sample, PaintMode mode) +static bool paint_smooth_stroke( + PaintStroke *stroke, const PaintSample *sample, PaintMode mode, + float r_mouse[2], float *r_pressure) { if (paint_supports_smooth_stroke(stroke->brush, mode)) { float radius = stroke->brush->smooth_stroke_radius * stroke->zoom_2d; - float u = stroke->brush->smooth_stroke_factor, v = 1.0f - u; - float dx = stroke->last_mouse_position[0] - sample->mouse[0]; - float dy = stroke->last_mouse_position[1] - sample->mouse[1]; + float u = stroke->brush->smooth_stroke_factor; /* If the mouse is moving within the radius of the last move, * don't update the mouse position. This allows sharp turns. */ - if (dx * dx + dy * dy < radius * radius) - return 0; + if (len_squared_v2v2(stroke->last_mouse_position, sample->mouse) < SQUARE(radius)) { + return false; + } - output[0] = sample->mouse[0] * v + stroke->last_mouse_position[0] * u; - output[1] = sample->mouse[1] * v + stroke->last_mouse_position[1] * u; - *outpressure = sample->pressure * v + stroke->last_pressure * u; + interp_v2_v2v2(r_mouse, sample->mouse, stroke->last_mouse_position, u); + *r_pressure = interpf(sample->pressure, stroke->last_pressure, u); } else { - output[0] = sample->mouse[0]; - output[1] = sample->mouse[1]; - *outpressure = sample->pressure; + r_mouse[0] = sample->mouse[0]; + r_mouse[1] = sample->mouse[1]; + *r_pressure = sample->pressure; } - return 1; + return true; } static float paint_space_stroke_spacing(const Scene *scene, PaintStroke *stroke, float size_pressure, float spacing_pressure) @@ -1190,7 +1189,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) /* airbrush */ ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER && event->customdata == stroke->timer)) { - if (paint_smooth_stroke(stroke, mouse, &pressure, &sample_average, mode)) { + if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, &pressure)) { if (stroke->stroke_started) { if (paint_space_stroke_enabled(br, mode)) { if (paint_space_stroke(C, op, mouse, pressure)) diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 87855879ec5..991025a4d5d 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -247,7 +247,7 @@ static bool make_vertexcol(Object *ob) /* single ob */ { Mesh *me; - if ((ob->id.lib) || + if (ID_IS_LINKED_DATABLOCK(ob) || ((me = BKE_mesh_from_object(ob)) == NULL) || (me->totpoly == 0) || (me->edit_btmesh)) @@ -257,9 +257,7 @@ static bool make_vertexcol(Object *ob) /* single ob */ /* copies from shadedisplist to mcol */ if (!me->mloopcol && me->totloop) { - if (!me->mloopcol) { - CustomData_add_layer(&me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop); - } + CustomData_add_layer(&me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop); BKE_mesh_update_customdata_pointers(me, true); } @@ -1841,7 +1839,7 @@ static int paint_poll_test(bContext *C) Object *ob = CTX_data_active_object(C); if (ob == NULL || ob->type != OB_MESH) return 0; - if (!ob->data || ((ID *)ob->data)->lib) + if (!ob->data || ID_IS_LINKED_DATABLOCK(ob->data)) return 0; if (CTX_data_edit_object(C)) return 0; @@ -2380,9 +2378,8 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P swap_m4m4(vc->rv3d->persmat, mat); /* calculate pivot for rotation around seletion if needed */ - if (U.uiflag & USER_ORBIT_SELECTION) { - paint_last_stroke_update(scene, vc->ar, mval); - } + /* also needed for "View Selected" on last stroke */ + paint_last_stroke_update(scene, vc->ar, mval); DAG_id_tag_update(ob->data, 0); ED_region_tag_redraw(vc->ar); @@ -2858,9 +2855,8 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P } /* calculate pivot for rotation around seletion if needed */ - if (U.uiflag & USER_ORBIT_SELECTION) { - paint_last_stroke_update(scene, vc->ar, mval); - } + /* also needed for "View Selected" on last stroke */ + paint_last_stroke_update(scene, vc->ar, mval); ED_region_tag_redraw(vc->ar); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 0931456058d..fe0fb3f5035 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1145,7 +1145,9 @@ static void calc_area_normal_and_center( /* Return modified brush strength. Includes the direction of the brush, positive * values pull vertices, negative values push. Uses tablet pressure and a * special multiplier found experimentally to scale the strength factor. */ -static float brush_strength(const Sculpt *sd, const StrokeCache *cache, const float feather, const UnifiedPaintSettings *ups) +static float brush_strength( + const Sculpt *sd, const StrokeCache *cache, + const float feather, const UnifiedPaintSettings *ups) { const Scene *scene = cache->vc->scene; const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); @@ -1237,7 +1239,7 @@ static float brush_strength(const Sculpt *sd, const StrokeCache *cache, const fl /* Return a multiplier for brush strength on a particular vertex. */ static float tex_strength(SculptSession *ss, Brush *br, - const float point[3], + const float brush_point[3], const float len, const short vno[3], const float fno[3], @@ -1249,6 +1251,9 @@ static float tex_strength(SculptSession *ss, Brush *br, MTex *mtex = &br->mtex; float avg = 1; float rgba[4]; + float point[3]; + + sub_v3_v3v3(point, brush_point, cache->plane_offset); if (!mtex->tex) { avg = 1; @@ -3985,7 +3990,9 @@ static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss) } /* Initialize the stroke cache invariants from operator properties */ -static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mouse[2]) +static void sculpt_update_cache_invariants( + bContext *C, Sculpt *sd, SculptSession *ss, + wmOperator *op, const float mouse[2]) { StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); Scene *scene = CTX_data_scene(C); @@ -4448,7 +4455,9 @@ static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin) } } -static float sculpt_raycast_init(ViewContext *vc, const float mouse[2], float ray_start[3], float ray_end[3], float ray_normal[3], bool original) +static float sculpt_raycast_init( + ViewContext *vc, const float mouse[2], + float ray_start[3], float ray_end[3], float ray_normal[3], bool original) { float obimat[4][4]; float dist; @@ -4956,8 +4965,9 @@ void sculpt_dyntopo_node_layers_add(SculptSession *ss) cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); } - ss->cd_vert_node_offset = CustomData_get_n_offset(&ss->bm->vdata, CD_PROP_INT, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT)); + ss->cd_vert_node_offset = CustomData_get_n_offset( + &ss->bm->vdata, CD_PROP_INT, + cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT)); ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; @@ -4967,8 +4977,9 @@ void sculpt_dyntopo_node_layers_add(SculptSession *ss) cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); } - ss->cd_face_node_offset = CustomData_get_n_offset(&ss->bm->pdata, CD_PROP_INT, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT)); + ss->cd_face_node_offset = CustomData_get_n_offset( + &ss->bm->pdata, CD_PROP_INT, + cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT)); ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; } @@ -5462,7 +5473,10 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) } if (message_unsupported == NULL) { + /* undo push is needed to prevent memory leak */ + sculpt_undo_push_begin("Dynamic topology enable"); sculpt_dynamic_topology_enable(C); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); } else { BKE_reportf(op->reports, RPT_WARNING, @@ -5664,7 +5678,8 @@ static void SCULPT_OT_sample_detail_size(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_int_array(ot->srna, "location", 2, NULL, 0, SHRT_MAX, "Location", "Screen Coordinates of sampling", 0, SHRT_MAX); + RNA_def_int_array(ot->srna, "location", 2, NULL, 0, SHRT_MAX, + "Location", "Screen Coordinates of sampling", 0, SHRT_MAX); } diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 4931426d62e..03f2e146b7d 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -136,7 +136,7 @@ static int sound_open_exec(bContext *C, wmOperator *op) if (pprop->prop) { /* when creating new ID blocks, use is already 1, but RNA - * pointer se also increases user, so this compensates it */ + * pointer use also increases user, so this compensates it */ id_us_min(&sound->id); RNA_id_pointer_create(&sound->id, &idptr); diff --git a/source/blender/editors/space_action/action_buttons.c b/source/blender/editors/space_action/action_buttons.c index 063a477d2b6..bfc808f6d83 100644 --- a/source/blender/editors/space_action/action_buttons.c +++ b/source/blender/editors/space_action/action_buttons.c @@ -122,7 +122,7 @@ void ACTION_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "ACTION_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = action_properties_toggle_exec; ot->poll = ED_operator_action_active; diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index a3be7791f9a..6755a6316cc 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -121,7 +121,7 @@ static bAction *action_create_new(bContext *C, bAction *oldact) */ if (oldact && GS(oldact->id.name) == ID_AC) { /* make a copy of the existing action */ - action = BKE_action_copy(oldact); + action = BKE_action_copy(CTX_data_main(C), oldact); } else { /* just make a new (empty) action */ diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index 10748c2fe15..9fc96e06299 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -81,7 +81,7 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - height = ((items * ACHANNEL_STEP) + (ACHANNEL_HEIGHT)); + height = ((items * ACHANNEL_STEP(ac)) + (ACHANNEL_HEIGHT(ac))); if (height > BLI_rcti_size_y(&v2d->mask)) { /* don't use totrect set, as the width stays the same * (NOTE: this is ok here, the configuration is pretty straightforward) @@ -95,11 +95,11 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) { /* first pass: just the standard GL-drawing for backdrop + text */ size_t channel_index = 0; - y = (float)ACHANNEL_FIRST; + y = (float)ACHANNEL_FIRST(ac); for (ale = anim_data.first; ale; ale = ale->next) { - float yminc = (float)(y - ACHANNEL_HEIGHT_HALF); - float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF); + float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); + float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* check if visible */ if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || @@ -110,7 +110,7 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) } /* adjust y-position for next one */ - y -= ACHANNEL_STEP; + y -= ACHANNEL_STEP(ac); channel_index++; } } @@ -118,11 +118,11 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) uiBlock *block = UI_block_begin(C, ar, __func__, UI_EMBOSS); size_t channel_index = 0; - y = (float)ACHANNEL_FIRST; + y = (float)ACHANNEL_FIRST(ac); for (ale = anim_data.first; ale; ale = ale->next) { - float yminc = (float)(y - ACHANNEL_HEIGHT_HALF); - float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF); + float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); + float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* check if visible */ if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || @@ -133,7 +133,7 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) } /* adjust y-position for next one */ - y -= ACHANNEL_STEP; + y -= ACHANNEL_STEP(ac); channel_index++; } @@ -195,19 +195,19 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - height = ((items * ACHANNEL_STEP) + (ACHANNEL_HEIGHT)); + height = ((items * ACHANNEL_STEP(ac)) + (ACHANNEL_HEIGHT(ac))); /* don't use totrect set, as the width stays the same * (NOTE: this is ok here, the configuration is pretty straightforward) */ v2d->tot.ymin = (float)(-height); /* first backdrop strips */ - y = (float)(-ACHANNEL_HEIGHT); + y = (float)(-ACHANNEL_HEIGHT(ac)); glEnable(GL_BLEND); for (ale = anim_data.first; ale; ale = ale->next) { - const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF); - const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF); + const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); + const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* check if visible */ if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || @@ -260,39 +260,39 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) } /* draw region twice: firstly backdrop, then the current range */ - glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF); + glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF(ac)); if (ac->datatype == ANIMCONT_ACTION) - glRectf(act_start, (float)y - ACHANNEL_HEIGHT_HALF, act_end, (float)y + ACHANNEL_HEIGHT_HALF); + glRectf(act_start, (float)y - ACHANNEL_HEIGHT_HALF(ac), act_end, (float)y + ACHANNEL_HEIGHT_HALF(ac)); } else if (ac->datatype == ANIMCONT_GPENCIL) { /* frames less than one get less saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); else glColor4ub(col2[0], col2[1], col2[2], 0x22); - glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF); + glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF(ac)); /* frames one and higher get a saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44); else glColor4ub(col2[0], col2[1], col2[2], 0x44); - glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF); + glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF(ac)); } else if (ac->datatype == ANIMCONT_MASK) { /* TODO --- this is a copy of gpencil */ /* frames less than one get less saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); else glColor4ub(col2[0], col2[1], col2[2], 0x22); - glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF); + glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF(ac)); /* frames one and higher get a saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44); else glColor4ub(col2[0], col2[1], col2[2], 0x44); - glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF); + glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF(ac)); } } } /* Increment the step */ - y -= ACHANNEL_STEP; + y -= ACHANNEL_STEP(ac); } glDisable(GL_BLEND); @@ -301,11 +301,11 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) * This is to try to optimize this for heavier data sets * 2) Keyframes which are out of view horizontally are disregarded */ - y = (float)(-ACHANNEL_HEIGHT); + y = (float)(-ACHANNEL_HEIGHT(ac)); for (ale = anim_data.first; ale; ale = ale->next) { - const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF); - const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF); + const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); + const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* check if visible */ if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || @@ -318,34 +318,34 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) /* draw 'keyframes' for each specific datatype */ switch (ale->datatype) { case ALE_ALL: - draw_summary_channel(v2d, ale->data, y); + draw_summary_channel(v2d, ale->data, y, ac->yscale_fac); break; case ALE_SCE: - draw_scene_channel(v2d, ads, ale->key_data, y); + draw_scene_channel(v2d, ads, ale->key_data, y, ac->yscale_fac); break; case ALE_OB: - draw_object_channel(v2d, ads, ale->key_data, y); + draw_object_channel(v2d, ads, ale->key_data, y, ac->yscale_fac); break; case ALE_ACT: - draw_action_channel(v2d, adt, ale->key_data, y); + draw_action_channel(v2d, adt, ale->key_data, y, ac->yscale_fac); break; case ALE_GROUP: - draw_agroup_channel(v2d, adt, ale->data, y); + draw_agroup_channel(v2d, adt, ale->data, y, ac->yscale_fac); break; case ALE_FCURVE: - draw_fcurve_channel(v2d, adt, ale->key_data, y); + draw_fcurve_channel(v2d, adt, ale->key_data, y, ac->yscale_fac); break; case ALE_GPFRAME: - draw_gpl_channel(v2d, ads, ale->data, y); + draw_gpl_channel(v2d, ads, ale->data, y, ac->yscale_fac); break; case ALE_MASKLAY: - draw_masklay_channel(v2d, ads, ale->data, y); + draw_masklay_channel(v2d, ads, ale->data, y, ac->yscale_fac); break; } } } - y -= ACHANNEL_STEP; + y -= ACHANNEL_STEP(ac); } /* free tempolary channels used for drawing */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 8261a211ed0..55b087c40e7 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -333,7 +333,7 @@ static bool actkeys_channels_get_selected_extents(bAnimContext *ac, float *min, ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through all channels, finding the first one that's selected */ - y = (float)ACHANNEL_FIRST; + y = (float)ACHANNEL_FIRST(ac); for (ale = anim_data.first; ale; ale = ale->next) { const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); @@ -343,8 +343,8 @@ static bool actkeys_channels_get_selected_extents(bAnimContext *ac, float *min, ANIM_channel_setting_get(ac, ale, ACHANNEL_SETTING_SELECT)) { /* update best estimate */ - *min = (float)(y - ACHANNEL_HEIGHT_HALF); - *max = (float)(y + ACHANNEL_HEIGHT_HALF); + *min = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); + *max = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* is this high enough priority yet? */ found = acf->channel_role; @@ -358,7 +358,7 @@ static bool actkeys_channels_get_selected_extents(bAnimContext *ac, float *min, } /* adjust y-position for next one */ - y -= ACHANNEL_STEP; + y -= ACHANNEL_STEP(ac); } /* free all temp data */ diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 7900217e28e..553be0ad290 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -213,7 +213,7 @@ static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, s KeyframeEditFunc ok_cb, select_cb; View2D *v2d = &ac->ar->v2d; rctf rectf; - float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF); + float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF(ac)); /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */ UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin + 2, &rectf.xmin, &rectf.ymin); @@ -239,7 +239,7 @@ static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, s AnimData *adt = ANIM_nla_mapping_get(ac, ale); /* get new vertical minimum extent of channel */ - ymin = ymax - ACHANNEL_STEP; + ymin = ymax - ACHANNEL_STEP(ac); /* set horizontal range (if applicable) */ if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) { @@ -392,7 +392,7 @@ static void region_select_action_keys(bAnimContext *ac, const rctf *rectf_view, KeyframeEditFunc ok_cb, select_cb; View2D *v2d = &ac->ar->v2d; rctf rectf, scaled_rectf; - float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF); + float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF(ac)); /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */ UI_view2d_region_to_view_rctf(v2d, rectf_view, &rectf); @@ -426,10 +426,10 @@ static void region_select_action_keys(bAnimContext *ac, const rctf *rectf_view, AnimData *adt = ANIM_nla_mapping_get(ac, ale); /* get new vertical minimum extent of channel */ - ymin = ymax - ACHANNEL_STEP; + ymin = ymax - ACHANNEL_STEP(ac); /* compute midpoint of channel (used for testing if the key is in the region or not) */ - ked.channel_y = ymin + ACHANNEL_HEIGHT_HALF; + ked.channel_y = ymin + ACHANNEL_HEIGHT_HALF(ac); /* if channel is mapped in NLA, apply correction * - Apply to the bounds being checked, not all the keyframe points, @@ -1351,6 +1351,7 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ bool found = false; float frame = 0.0f; /* frame of keyframe under mouse - NLA corrections not applied/included */ float selx = 0.0f; /* frame of keyframe under mouse */ + float key_hsize; float x, y; rctf rectf; @@ -1360,11 +1361,16 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ /* use View2D to determine the index of the channel (i.e a row in the list) where keyframe was */ UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y); - UI_view2d_listview_view_to_cell(v2d, 0, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index); + UI_view2d_listview_view_to_cell(v2d, 0, ACHANNEL_STEP(ac), 0, (float)ACHANNEL_HEIGHT_HALF(ac), x, y, NULL, &channel_index); - /* x-range to check is +/- 7 (in screen/region-space) on either side of mouse click (size of keyframe icon) */ - UI_view2d_region_to_view(v2d, mval[0] - 7, mval[1], &rectf.xmin, &rectf.ymin); - UI_view2d_region_to_view(v2d, mval[0] + 7, mval[1], &rectf.xmax, &rectf.ymax); + /* x-range to check is +/- 7px for standard keyframe under standard dpi/y-scale (in screen/region-space), + * on either side of mouse click (size of keyframe icon) + */ + key_hsize = ACHANNEL_HEIGHT(ac) * 0.8f; /* standard channel height (to allow for some slop) */ + key_hsize = roundf(key_hsize / 2.0f); /* half-size (for either side), but rounded up to nearest int (for easier targetting) */ + + UI_view2d_region_to_view(v2d, mval[0] - (int)key_hsize, mval[1], &rectf.xmin, &rectf.ymin); + UI_view2d_region_to_view(v2d, mval[0] + (int)key_hsize, mval[1], &rectf.xmax, &rectf.ymax); /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 1ff656243d6..14d0f909d23 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -634,7 +634,7 @@ typedef struct PrefetchQueue { short render_size, render_flag; /* If true prefecthing goes forward in time, - * othwewise it goes backwards in time (starting from current frame). + * otherwise it goes backwards in time (starting from current frame). */ bool forward; diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 085fdd57309..83876ae2669 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -234,7 +234,7 @@ static int open_exec(bContext *C, wmOperator *op) if (pprop->prop) { /* when creating new ID blocks, use is already 1, but RNA - * pointer se also increases user, so this compensates it */ + * pointer use also increases user, so this compensates it */ id_us_min(&clip->id); RNA_id_pointer_create(&clip->id, &idptr); diff --git a/source/blender/editors/space_clip/clip_toolbar.c b/source/blender/editors/space_clip/clip_toolbar.c index d31d7f53b30..b042bbe8fea 100644 --- a/source/blender/editors/space_clip/clip_toolbar.c +++ b/source/blender/editors/space_clip/clip_toolbar.c @@ -106,7 +106,7 @@ void CLIP_OT_properties(wmOperatorType *ot) { /* identifiers */ ot->name = "Properties"; - ot->description = "Toggle clip properties panel"; + ot->description = "Toggle the properties region visibility"; ot->idname = "CLIP_OT_properties"; /* api callbacks */ diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 415839ab761..396d71f0a20 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -818,7 +818,7 @@ static void clip_keymap(struct wmKeyConfig *keyconf) keymap = WM_keymap_find(keyconf, "Clip Dopesheet Editor", SPACE_CLIP, 0); - kmi = WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_select_channel", ACTIONMOUSE, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_select_channel", LEFTMOUSE, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "extend", true); /* toggle */ WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_view_all", HOMEKEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 175d83506c6..ab33d452d3c 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -618,7 +618,7 @@ int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matche */ for (i = 0; i < n; i++) { file = filelist_file(sfile->files, i); - /* Do not check wether file is a file or dir here! Causes T44243 (we do accept dirs at this stage). */ + /* Do not check whether file is a file or dir here! Causes T44243 (we do accept dirs at this stage). */ if (fnmatch(pattern, file->relpath, 0) == 0) { filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); if (!match) { diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index a9ab1502e16..516814b63b4 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -812,6 +812,11 @@ static void graph_panel_drivers(const bContext *C, Panel *pa) } col = uiLayoutColumn(pa->layout, true); + + if (driver->type == DRIVER_TYPE_PYTHON) { + uiItemR(col, &driver_ptr, "use_self", 0, NULL, ICON_NONE); + } + /* debug setting */ uiItemR(col, &driver_ptr, "show_debug_info", 0, NULL, ICON_NONE); @@ -1062,7 +1067,7 @@ void GRAPH_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "GRAPH_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = graph_properties_toggle_exec; ot->poll = ED_operator_graphedit_active; diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index 3ac90200aea..ae91a466495 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -826,7 +826,7 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu) //if ((driver->flag & DRIVER_FLAG_SHOWDEBUG) == 0) // return; - /* No curve to modify/visualise the result? + /* No curve to modify/visualize the result? * => We still want to show the 1-1 default... */ if ((fcu->totvert == 0) && BLI_listbase_is_empty(&fcu->modifiers)) { @@ -1130,18 +1130,18 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) * - offset of ACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for * start of list offset, and the second is as a correction for the scrollers. */ - height = (float)((items * ACHANNEL_STEP) + (ACHANNEL_HEIGHT * 2)); + height = (float)((items * ACHANNEL_STEP(ac)) + (ACHANNEL_HEIGHT(ac) * 2)); UI_view2d_totRect_set(v2d, ar->winx, height); /* loop through channels, and set up drawing depending on their type */ { /* first pass: just the standard GL-drawing for backdrop + text */ size_t channel_index = 0; - y = (float)ACHANNEL_FIRST; + y = (float)ACHANNEL_FIRST(ac); for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) { - const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF); - const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF); + const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); + const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* check if visible */ if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || @@ -1152,7 +1152,7 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) } /* adjust y-position for next one */ - y -= ACHANNEL_STEP; + y -= ACHANNEL_STEP(ac); channel_index++; } } @@ -1160,15 +1160,15 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) uiBlock *block = UI_block_begin(C, ar, __func__, UI_EMBOSS); size_t channel_index = 0; - y = (float)ACHANNEL_FIRST; + y = (float)ACHANNEL_FIRST(ac); /* set blending again, as may not be set in previous step */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) { - const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF); - const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF); + const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); + const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* check if visible */ if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || @@ -1179,7 +1179,7 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) } /* adjust y-position for next one */ - y -= ACHANNEL_STEP; + y -= ACHANNEL_STEP(ac); channel_index++; } diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 8ae5932f3fd..2582ba4be8d 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -525,12 +525,130 @@ static void graph_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn) } } - +/* Update F-Curve colors */ +static void graph_refresh_fcurve_colors(const bContext *C) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + size_t items; + int filter; + int i; + + if (ANIM_animdata_get_context(C, &ac) == false) + return; + + UI_SetTheme(SPACE_IPO, RGN_TYPE_WINDOW); + + /* build list of F-Curves which will be visible as channels in channel-region + * - we don't include ANIMFILTER_CURVEVISIBLE filter, as that will result in a + * mismatch between channel-colors and the drawn curves + */ + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS); + items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* loop over F-Curves, assigning colors */ + for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) { + FCurve *fcu = (FCurve *)ale->data; + + /* set color of curve here */ + switch (fcu->color_mode) { + case FCURVE_COLOR_CUSTOM: + { + /* User has defined a custom color for this curve already (we assume it's not going to cause clashes with text colors), + * which should be left alone... Nothing needs to be done here. + */ + break; + } + case FCURVE_COLOR_AUTO_RGB: + { + /* F-Curve's array index is automatically mapped to RGB values. This works best of 3-value vectors. + * TODO: find a way to module the hue so that not all curves have same color... + */ + float *col = fcu->color; + + switch (fcu->array_index) { + case 0: + UI_GetThemeColor3fv(TH_AXIS_X, col); + break; + case 1: + UI_GetThemeColor3fv(TH_AXIS_Y, col); + break; + case 2: + UI_GetThemeColor3fv(TH_AXIS_Z, col); + break; + default: + /* 'unknown' color - bluish so as to not conflict with handles */ + col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f; + break; + } + break; + } + case FCURVE_COLOR_AUTO_YRGB: + { + /* Like FCURVE_COLOR_AUTO_RGB, except this is for quaternions... */ + float *col = fcu->color; + + switch (fcu->array_index) { + case 1: + UI_GetThemeColor3fv(TH_AXIS_X, col); + break; + case 2: + UI_GetThemeColor3fv(TH_AXIS_Y, col); + break; + case 3: + UI_GetThemeColor3fv(TH_AXIS_Z, col); + break; + + case 0: + { + /* Special Case: "W" channel should be yellowish, so blend X and Y channel colors... */ + float c1[3], c2[3]; + float h1[3], h2[3]; + float hresult[3]; + + /* - get colors (rgb) */ + UI_GetThemeColor3fv(TH_AXIS_X, c1); + UI_GetThemeColor3fv(TH_AXIS_Y, c2); + + /* - perform blending in HSV space (to keep brightness similar) */ + rgb_to_hsv_v(c1, h1); + rgb_to_hsv_v(c2, h2); + + interp_v3_v3v3(hresult, h1, h2, 0.5f); + + /* - convert back to RGB for display */ + hsv_to_rgb_v(hresult, col); + break; + } + + default: + /* 'unknown' color - bluish so as to not conflict with handles */ + col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f; + break; + } + break; + } + case FCURVE_COLOR_AUTO_RAINBOW: + default: + { + /* determine color 'automatically' using 'magic function' which uses the given args + * of current item index + total items to determine some RGB color + */ + getcolor_fcurve_rainbow(i, items, fcu->color); + break; + } + } + } + + /* free temp list */ + ANIM_animdata_freelist(&anim_data); +} static void graph_refresh(const bContext *C, ScrArea *sa) { SpaceIpo *sipo = (SpaceIpo *)sa->spacedata.first; - bAnimContext ac; /* updates to data needed depends on Graph Editor mode... */ switch (sipo->mode) { @@ -558,119 +676,7 @@ static void graph_refresh(const bContext *C, ScrArea *sa) } /* init/adjust F-Curve colors */ - if (ANIM_animdata_get_context(C, &ac)) { - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - size_t items; - int filter; - int i; - - UI_SetTheme(SPACE_IPO, RGN_TYPE_WINDOW); - - /* build list of F-Curves which will be visible as channels in channel-region - * - we don't include ANIMFILTER_CURVEVISIBLE filter, as that will result in a - * mismatch between channel-colors and the drawn curves - */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS); - items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - /* loop over F-Curves, assigning colors */ - for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) { - FCurve *fcu = (FCurve *)ale->data; - - /* set color of curve here */ - switch (fcu->color_mode) { - case FCURVE_COLOR_CUSTOM: - { - /* User has defined a custom color for this curve already (we assume it's not going to cause clashes with text colors), - * which should be left alone... Nothing needs to be done here. - */ - break; - } - case FCURVE_COLOR_AUTO_RGB: - { - /* F-Curve's array index is automatically mapped to RGB values. This works best of 3-value vectors. - * TODO: find a way to module the hue so that not all curves have same color... - */ - float *col = fcu->color; - - switch (fcu->array_index) { - case 0: - UI_GetThemeColor3fv(TH_AXIS_X, col); - break; - case 1: - UI_GetThemeColor3fv(TH_AXIS_Y, col); - break; - case 2: - UI_GetThemeColor3fv(TH_AXIS_Z, col); - break; - default: - /* 'unknown' color - bluish so as to not conflict with handles */ - col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f; - break; - } - break; - } - case FCURVE_COLOR_AUTO_YRGB: - { - /* Like FCURVE_COLOR_AUTO_RGB, except this is for quaternions... */ - float *col = fcu->color; - - switch (fcu->array_index) { - case 1: - UI_GetThemeColor3fv(TH_AXIS_X, col); - break; - case 2: - UI_GetThemeColor3fv(TH_AXIS_Y, col); - break; - case 3: - UI_GetThemeColor3fv(TH_AXIS_Z, col); - break; - - case 0: - { - /* Special Case: "W" channel should be yellowish, so blend X and Y channel colors... */ - float c1[3], c2[3]; - float h1[3], h2[3]; - float hresult[3]; - - /* - get colors (rgb) */ - UI_GetThemeColor3fv(TH_AXIS_X, c1); - UI_GetThemeColor3fv(TH_AXIS_Y, c2); - - /* - perform blending in HSV space (to keep brightness similar) */ - rgb_to_hsv_v(c1, h1); - rgb_to_hsv_v(c2, h2); - - interp_v3_v3v3(hresult, h1, h2, 0.5f); - - /* - convert back to RGB for display */ - hsv_to_rgb_v(hresult, col); - break; - } - - default: - /* 'unknown' color - bluish so as to not conflict with handles */ - col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f; - break; - } - break; - } - case FCURVE_COLOR_AUTO_RAINBOW: - default: - { - /* determine color 'automatically' using 'magic function' which uses the given args - * of current item index + total items to determine some RGB color - */ - getcolor_fcurve_rainbow(i, items, fcu->color); - break; - } - } - } - - /* free temp list */ - ANIM_animdata_freelist(&anim_data); - } + graph_refresh_fcurve_colors(C); } static void graph_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index a2db6827b0e..b9d98dfe794 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -345,6 +345,13 @@ struct ImageUI_Data { int rpass_index; }; +static struct ImageUI_Data *ui_imageuser_data_copy(const struct ImageUI_Data *rnd_pt_src) +{ + struct ImageUI_Data *rnd_pt_dst = MEM_mallocN(sizeof(*rnd_pt_src), __func__); + memcpy(rnd_pt_dst, rnd_pt_src, sizeof(*rnd_pt_src)); + return rnd_pt_dst; +} + static void ui_imageuser_layer_menu(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt) { struct ImageUI_Data *rnd_data = rnd_pt; @@ -532,9 +539,10 @@ static void ui_imageuser_view_menu_multiview(bContext *UNUSED(C), uiLayout *layo } /* 5 layer button callbacks... */ -static void image_multi_cb(bContext *C, void *rr_v, void *iuser_v) +static void image_multi_cb(bContext *C, void *rnd_pt, void *rr_v) { - ImageUser *iuser = iuser_v; + struct ImageUI_Data *rnd_data = rnd_pt; + ImageUser *iuser = rnd_data->iuser; BKE_image_multilayer_index(rr_v, iuser); WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); @@ -575,6 +583,8 @@ static bool ui_imageuser_layer_menu_step(bContext *C, int direction, void *rnd_p BLI_assert(0); } + BKE_image_release_renderresult(scene, image); + if (changed) { BKE_image_multilayer_index(rr, iuser); WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); @@ -662,10 +672,11 @@ static bool ui_imageuser_pass_menu_step(bContext *C, int direction, void *rnd_pt } /* 5 view button callbacks... */ -static void image_multiview_cb(bContext *C, void *ima_v, void *iuser_v) +static void image_multiview_cb(bContext *C, void *rnd_pt, void *UNUSED(arg_v)) { - Image *ima = ima_v; - ImageUser *iuser = iuser_v; + struct ImageUI_Data *rnd_data = rnd_pt; + Image *ima = rnd_data->image; + ImageUser *iuser = rnd_data->iuser; BKE_image_multiview_index(ima, iuser); WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); @@ -692,7 +703,7 @@ static void uiblock_layer_pass_buttons( uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, int w, short *render_slot) { - static struct ImageUI_Data rnd_pt; /* XXX, workaround */ + struct ImageUI_Data rnd_pt_local, *rnd_pt = NULL; uiBlock *block = uiLayoutGetBlock(layout); uiBut *but; RenderLayer *rl = NULL; @@ -708,10 +719,10 @@ static void uiblock_layer_pass_buttons( wmenu2 = (3 * w) / 5; wmenu3 = (3 * w) / 6; wmenu4 = (3 * w) / 6; - - rnd_pt.image = image; - rnd_pt.iuser = iuser; - rnd_pt.rpass_index = 0; + + rnd_pt_local.image = image; + rnd_pt_local.iuser = iuser; + rnd_pt_local.rpass_index = 0; /* menu buts */ if (render_slot) { @@ -723,10 +734,12 @@ static void uiblock_layer_pass_buttons( BLI_snprintf(str, sizeof(str), IFACE_("Slot %d"), *render_slot + 1); } + rnd_pt = ui_imageuser_data_copy(&rnd_pt_local); but = uiDefMenuBut(block, ui_imageuser_slot_menu, image, str, 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select Slot")); UI_but_func_menu_step_set(but, ui_imageuser_slot_menu_step); - UI_but_func_set(but, image_multi_cb, rr, iuser); + UI_but_funcN_set(but, image_multi_cb, rnd_pt, rr); UI_but_type_set_menu_from_pulldown(but); + rnd_pt = NULL; } if (rr) { @@ -738,15 +751,18 @@ static void uiblock_layer_pass_buttons( fake_name = ui_imageuser_layer_fake_name(rr); rpass_index = iuser->layer - (fake_name ? 1 : 0); rl = BLI_findlink(&rr->layers, rpass_index); - rnd_pt.rpass_index = rpass_index; + rnd_pt_local.rpass_index = rpass_index; if (RE_layers_have_name(rr)) { display_name = rl ? rl->name : (fake_name ? fake_name : ""); - but = uiDefMenuBut(block, ui_imageuser_layer_menu, &rnd_pt, display_name, - 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer")); + rnd_pt = ui_imageuser_data_copy(&rnd_pt_local); + but = uiDefMenuBut( + block, ui_imageuser_layer_menu, rnd_pt, display_name, + 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer")); UI_but_func_menu_step_set(but, ui_imageuser_layer_menu_step); - UI_but_func_set(but, image_multi_cb, rr, iuser); + UI_but_funcN_set(but, image_multi_cb, rnd_pt, rr); UI_but_type_set_menu_from_pulldown(but); + rnd_pt = NULL; } /* pass */ @@ -754,11 +770,14 @@ static void uiblock_layer_pass_buttons( rpass = (rl ? BLI_findlink(&rl->passes, iuser->pass - (fake_name ? 1 : 0)) : NULL); display_name = rpass ? rpass->internal_name : (fake_name ? fake_name : ""); - but = uiDefMenuBut(block, ui_imageuser_pass_menu, &rnd_pt, IFACE_(display_name), - 0, 0, wmenu3, UI_UNIT_Y, TIP_("Select Pass")); + rnd_pt = ui_imageuser_data_copy(&rnd_pt_local); + but = uiDefMenuBut( + block, ui_imageuser_pass_menu, rnd_pt, IFACE_(display_name), + 0, 0, wmenu3, UI_UNIT_Y, TIP_("Select Pass")); UI_but_func_menu_step_set(but, ui_imageuser_pass_menu_step); - UI_but_func_set(but, image_multi_cb, rr, iuser); + UI_but_funcN_set(but, image_multi_cb, rnd_pt, rr); UI_but_type_set_menu_from_pulldown(but); + rnd_pt = NULL; /* view */ if (BLI_listbase_count_ex(&rr->views, 2) > 1 && @@ -767,9 +786,13 @@ static void uiblock_layer_pass_buttons( rview = BLI_findlink(&rr->views, iuser->view); display_name = rview ? rview->name : ""; - but = uiDefMenuBut(block, ui_imageuser_view_menu_rr, &rnd_pt, display_name, 0, 0, wmenu4, UI_UNIT_Y, TIP_("Select View")); - UI_but_func_set(but, image_multi_cb, rr, iuser); + rnd_pt = ui_imageuser_data_copy(&rnd_pt_local); + but = uiDefMenuBut( + block, ui_imageuser_view_menu_rr, rnd_pt, display_name, + 0, 0, wmenu4, UI_UNIT_Y, TIP_("Select View")); + UI_but_funcN_set(but, image_multi_cb, rnd_pt, rr); UI_but_type_set_menu_from_pulldown(but); + rnd_pt = NULL; } } @@ -787,9 +810,13 @@ static void uiblock_layer_pass_buttons( } } - but = uiDefMenuBut(block, ui_imageuser_view_menu_multiview, &rnd_pt, display_name, 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select View")); - UI_but_func_set(but, image_multiview_cb, image, iuser); + rnd_pt = ui_imageuser_data_copy(&rnd_pt_local); + but = uiDefMenuBut( + block, ui_imageuser_view_menu_multiview, rnd_pt, display_name, + 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select View")); + UI_but_funcN_set(but, image_multiview_cb, rnd_pt, NULL); UI_but_type_set_menu_from_pulldown(but); + rnd_pt = NULL; } } @@ -1303,7 +1330,7 @@ void IMAGE_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "IMAGE_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = image_properties_toggle_exec; ot->poll = ED_operator_image_active; diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 06caf930988..1158e692182 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1061,6 +1061,12 @@ typedef struct ImageOpenData { ImageFormatData im_format; } ImageOpenData; +typedef struct ImageFrameRange { + struct ImageFrameRange *next, *prev; + ListBase frames; + char filepath[FILE_MAX]; +} ImageFrameRange; + typedef struct ImageFrame { struct ImageFrame *next, *prev; int framenr; @@ -1086,10 +1092,10 @@ static void image_open_cancel(bContext *UNUSED(C), wmOperator *op) * \param frames [out] the list of frame numbers found in the files matching the first one by name * \param path [out] the full path of the first file in the list of image files */ -static void image_sequence_get_frames(PointerRNA *ptr, ListBase *frames, char *path, const size_t maxlen) +static void image_sequence_get_frame_ranges(PointerRNA *ptr, ListBase *frames_all) { char dir[FILE_MAXDIR]; - bool is_first_entry = true; + ImageFrameRange *frame_range = NULL; RNA_string_get(ptr, "directory", dir); RNA_BEGIN (ptr, itemptr, "files") @@ -1101,29 +1107,26 @@ static void image_sequence_get_frames(PointerRNA *ptr, ListBase *frames, char *p ImageFrame *frame = MEM_callocN(sizeof(ImageFrame), "image_frame"); /* use the first file in the list as base filename */ - if (is_first_entry) { - BLI_join_dirfile(path, maxlen, dir, filename); - frame->framenr = BLI_stringdec(filename, base_head, base_tail, &digits); - BLI_addtail(frames, frame); - is_first_entry = false; + frame->framenr = BLI_stringdec(filename, head, tail, &digits); + + /* still in the same sequence */ + if ((frame_range != NULL) && + (STREQLEN(base_head, head, FILE_MAX)) && + (STREQLEN(base_tail, tail, FILE_MAX))) + { + /* pass */ } else { - frame->framenr = BLI_stringdec(filename, head, tail, &digits); + /* start a new frame range */ + frame_range = MEM_callocN(sizeof(*frame_range), __func__); + BLI_join_dirfile(frame_range->filepath, sizeof(frame_range->filepath), dir, filename); + BLI_addtail(frames_all, frame_range); - /* still in the same sequence */ - if ((STREQLEN(base_head, head, FILE_MAX)) && - (STREQLEN(base_tail, tail, FILE_MAX))) - { - BLI_addtail(frames, frame); - } - else { - /* different file base name found, is ignored */ - MEM_freeN(filename); - MEM_freeN(frame); - break; - } + BLI_strncpy(base_head, head, sizeof(base_head)); + BLI_strncpy(base_tail, tail, sizeof(base_tail)); } + BLI_addtail(&frame_range->frames, frame); MEM_freeN(filename); } RNA_END @@ -1164,6 +1167,52 @@ static int image_sequence_get_len(ListBase *frames, int *ofs) return 0; } +static Image *image_open_single( + wmOperator *op, const char *filepath, const char *relbase, + bool is_relative_path, bool use_multiview, int frame_seq_len) +{ + bool exists = false; + Image *ima = NULL; + + errno = 0; + ima = BKE_image_load_exists_ex(filepath, &exists); + + if (!ima) { + if (op->customdata) MEM_freeN(op->customdata); + BKE_reportf(op->reports, RPT_ERROR, "Cannot read '%s': %s", + filepath, errno ? strerror(errno) : TIP_("unsupported image format")); + return NULL; + } + + if (!exists) { + /* only image path after save, never ibuf */ + if (is_relative_path) { + BLI_path_rel(ima->name, relbase); + } + + /* handle multiview images */ + if (use_multiview) { + ImageOpenData *iod = op->customdata; + ImageFormatData *imf = &iod->im_format; + + ima->flag |= IMA_USE_VIEWS; + ima->views_format = imf->views_format; + *ima->stereo3d_format = imf->stereo3d_format; + } + else { + ima->flag &= ~IMA_USE_VIEWS; + BKE_image_free_views(ima); + } + + if ((frame_seq_len > 1) && (ima->source == IMA_SRC_FILE)) { + ima->source = IMA_SRC_SEQUENCE; + } + } + + return ima; +} + + static int image_open_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -1174,70 +1223,60 @@ static int image_open_exec(bContext *C, wmOperator *op) ImageOpenData *iod = op->customdata; PointerRNA idptr; Image *ima = NULL; - char path[FILE_MAX]; + char filepath[FILE_MAX]; int frame_seq_len = 0; int frame_ofs = 1; - bool exists = false; const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path"); + const bool use_multiview = RNA_boolean_get(op->ptr, "use_multiview"); - RNA_string_get(op->ptr, "filepath", path); + if (!op->customdata) + image_open_init(C, op); + + RNA_string_get(op->ptr, "filepath", filepath); if (RNA_struct_property_is_set(op->ptr, "directory") && RNA_struct_property_is_set(op->ptr, "files")) { - /* only to pass to imbuf */ - char path_full[FILE_MAX]; - BLI_strncpy(path_full, path, sizeof(path_full)); - BLI_path_abs(path_full, G.main->name); + bool was_relative = BLI_path_is_rel(filepath); + ListBase frame_ranges_all; - if (!IMB_isanim(path_full)) { - bool was_relative = BLI_path_is_rel(path); - ListBase frames; + BLI_listbase_clear(&frame_ranges_all); + image_sequence_get_frame_ranges(op->ptr, &frame_ranges_all); + for (ImageFrameRange *frame_range = frame_ranges_all.first; frame_range; frame_range = frame_range->next) { + int frame_range_ofs; + int frame_range_seq_len = image_sequence_get_len(&frame_range->frames, &frame_range_ofs); + BLI_freelistN(&frame_range->frames); - BLI_listbase_clear(&frames); - image_sequence_get_frames(op->ptr, &frames, path, sizeof(path)); - frame_seq_len = image_sequence_get_len(&frames, &frame_ofs); - BLI_freelistN(&frames); + char filepath_range[FILE_MAX]; + BLI_strncpy(filepath_range, frame_range->filepath, sizeof(filepath_range)); if (was_relative) { - BLI_path_rel(path, G.main->name); + BLI_path_rel(filepath_range, bmain->name); } - } - } - - errno = 0; - ima = BKE_image_load_exists_ex(path, &exists); + Image *ima_range = image_open_single( + op, filepath_range, bmain->name, + is_relative_path, use_multiview, frame_range_seq_len); - if (!ima) { - if (op->customdata) MEM_freeN(op->customdata); - BKE_reportf(op->reports, RPT_ERROR, "Cannot read '%s': %s", - path, errno ? strerror(errno) : TIP_("unsupported image format")); - return OPERATOR_CANCELLED; - } - - if (!op->customdata) - image_open_init(C, op); - - /* handle multiview images */ - if (RNA_boolean_get(op->ptr, "use_multiview")) { - ImageFormatData *imf = &iod->im_format; - - ima->flag |= IMA_USE_VIEWS; - ima->views_format = imf->views_format; - *ima->stereo3d_format = imf->stereo3d_format; + /* take the first image */ + if ((ima == NULL) && ima_range) { + ima = ima_range; + frame_seq_len = frame_range_seq_len; + frame_ofs = frame_range_ofs; + } + } + BLI_freelistN(&frame_ranges_all); } else { - ima->flag &= ~IMA_USE_VIEWS; - BKE_image_free_views(ima); + /* for drag & drop etc. */ + ima = image_open_single( + op, filepath, bmain->name, + is_relative_path, use_multiview, 1); } - /* only image path after save, never ibuf */ - if (is_relative_path) { - if (!exists) { - BLI_path_rel(ima->name, bmain->name); - } + if (ima == NULL) { + return OPERATOR_CANCELLED; } /* hook into UI */ @@ -1245,11 +1284,9 @@ static int image_open_exec(bContext *C, wmOperator *op) if (iod->pprop.prop) { /* when creating new ID blocks, use is already 1, but RNA - * pointer se also increases user, so this compensates it */ + * pointer use also increases user, so this compensates it */ id_us_min(&ima->id); - if ((frame_seq_len > 1) && ima->source == IMA_SRC_FILE) { - ima->source = IMA_SRC_SEQUENCE; - } + RNA_id_pointer_create(&ima->id, &idptr); RNA_property_pointer_set(&iod->pprop.ptr, iod->pprop.prop, idptr); RNA_property_update(C, &iod->pprop.ptr, iod->pprop.prop); @@ -2359,7 +2396,7 @@ static int image_new_exec(bContext *C, wmOperator *op) if (prop) { /* when creating new ID blocks, use is already 1, but RNA - * pointer se also increases user, so this compensates it */ + * pointer use also increases user, so this compensates it */ id_us_min(&ima->id); RNA_id_pointer_create(&ima->id, &idptr); diff --git a/source/blender/editors/space_logic/logic_buttons.c b/source/blender/editors/space_logic/logic_buttons.c index 12c7ef3d3ec..e5eee21ed08 100644 --- a/source/blender/editors/space_logic/logic_buttons.c +++ b/source/blender/editors/space_logic/logic_buttons.c @@ -63,7 +63,7 @@ static int logic_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op)) void LOGIC_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->idname = "LOGIC_OT_properties"; ot->exec = logic_properties_toggle_exec; diff --git a/source/blender/editors/space_logic/logic_ops.c b/source/blender/editors/space_logic/logic_ops.c index 51f4a61047d..074368a82c5 100644 --- a/source/blender/editors/space_logic/logic_ops.c +++ b/source/blender/editors/space_logic/logic_ops.c @@ -65,7 +65,7 @@ static int edit_sensor_poll(bContext *C) { PointerRNA ptr = CTX_data_pointer_get_type(C, "sensor", &RNA_Sensor); - if (ptr.data && ((ID *)ptr.id.data)->lib) return 0; + if (ptr.data && ID_IS_LINKED_DATABLOCK(ptr.id.data)) return 0; return 1; } @@ -73,7 +73,7 @@ static int edit_controller_poll(bContext *C) { PointerRNA ptr = CTX_data_pointer_get_type(C, "controller", &RNA_Controller); - if (ptr.data && ((ID *)ptr.id.data)->lib) return 0; + if (ptr.data && ID_IS_LINKED_DATABLOCK(ptr.id.data)) return 0; return 1; } @@ -81,7 +81,7 @@ static int edit_actuator_poll(bContext *C) { PointerRNA ptr = CTX_data_pointer_get_type(C, "actuator", &RNA_Actuator); - if (ptr.data && ((ID *)ptr.id.data)->lib) return 0; + if (ptr.data && ID_IS_LINKED_DATABLOCK(ptr.id.data)) return 0; return 1; } diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index cbdc476bee6..9032d286933 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -567,7 +567,7 @@ void NLA_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "NLA_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = nla_properties_toggle_exec; ot->poll = ED_operator_nla_active; diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index b0adabe4d1d..5b3c062e16d 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -62,7 +62,7 @@ #include "UI_resources.h" #include "UI_view2d.h" - +#include "nla_private.h" #include "nla_intern.h" /* own include */ @@ -145,6 +145,62 @@ static void nla_action_draw_keyframes(AnimData *adt, bAction *act, View2D *v2d, BLI_dlrbTree_free(&keys); } +/* Strip Markers ------------------------ */ + +/* Markers inside an action strip */ +static void nla_actionclip_draw_markers(NlaStrip *strip, float yminc, float ymaxc) +{ + bAction *act = strip->act; + TimeMarker *marker; + + if (ELEM(NULL, strip->act, strip->act->markers.first)) + return; + + for (marker = act->markers.first; marker; marker = marker->next) { + if ((marker->frame > strip->actstart) && (marker->frame < strip->actend)) { + float frame = nlastrip_get_frame(strip, marker->frame, NLATIME_CONVERT_MAP); + + /* just a simple line for now */ + // XXX: draw a triangle instead... + fdrawline(frame, yminc + 1, frame, ymaxc - 1); + } + } +} + +/* Markers inside a NLA-Strip */ +static void nla_strip_draw_markers(NlaStrip *strip, float yminc, float ymaxc) +{ + glLineWidth(2.0); + + if (strip->type == NLASTRIP_TYPE_CLIP) { + /* try not to be too conspicuous, while being visible enough when transforming */ + if (strip->flag & NLASTRIP_FLAG_SELECT) + UI_ThemeColorShade(TH_STRIP_SELECT, -60); + else + UI_ThemeColorShade(TH_STRIP_SELECT, -40); + + setlinestyle(3); + + /* just draw the markers in this clip */ + nla_actionclip_draw_markers(strip, yminc, ymaxc); + + setlinestyle(0); + } + else if (strip->flag & NLASTRIP_FLAG_TEMP_META) { + /* just a solid color, so that it is very easy to spot */ + UI_ThemeColorShade(TH_STRIP_SELECT, 20); + + /* draw the markers in the first level of strips only (if they are actions) */ + for (NlaStrip *nls = strip->strips.first; nls; nls = nls->next) { + if (nls->type == NLASTRIP_TYPE_CLIP) { + nla_actionclip_draw_markers(nls, yminc, ymaxc); + } + } + } + + glLineWidth(1.0); +} + /* Strips (Proper) ---------------------- */ /* get colors for drawing NLA-Strips */ @@ -361,6 +417,10 @@ static void nla_draw_strip(SpaceNla *snla, AnimData *adt, NlaTrack *nlt, NlaStri nla_draw_strip_curves(strip, yminc, ymaxc); + /* draw markings indicating locations of local markers (useful for lining up different actions) */ + if ((snla->flag & SNLA_NOLOCALMARKERS) == 0) + nla_strip_draw_markers(strip, yminc, ymaxc); + /* draw strip outline * - color used here is to indicate active vs non-active */ diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index a2159696394..3e7e8ccc3f4 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -1856,6 +1856,7 @@ void NLA_OT_action_sync_length(wmOperatorType *ot) static int nlaedit_make_single_user_exec(bContext *C, wmOperator *UNUSED(op)) { + Main *bmain = CTX_data_main(C); bAnimContext ac; ListBase anim_data = {NULL, NULL}; @@ -1887,7 +1888,7 @@ static int nlaedit_make_single_user_exec(bContext *C, wmOperator *UNUSED(op)) /* multi-user? */ if (ID_REAL_USERS(strip->act) > 1) { /* make a new copy of the action for us to use (it will have 1 user already) */ - bAction *new_action = BKE_action_copy(strip->act); + bAction *new_action = BKE_action_copy(bmain, strip->act); /* decrement user count of our existing action */ id_us_min(&strip->act->id); @@ -1945,6 +1946,7 @@ static short bezt_apply_nlamapping(KeyframeEditData *ked, BezTriple *bezt) static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op)) { + Main *bmain = CTX_data_main(C); bAnimContext ac; ListBase anim_data = {NULL, NULL}; @@ -1974,7 +1976,7 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op)) continue; if (strip->act->id.us > 1) { /* make a copy of the Action to work on */ - bAction *act = BKE_action_copy(strip->act); + bAction *act = BKE_action_copy(bmain, strip->act); /* set this as the new referenced action, decrementing the users of the old one */ id_us_min(&strip->act->id); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index ddbd07616bc..7b08b8368ba 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1338,7 +1338,7 @@ static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, Point scn_ptr = RNA_pointer_get(ptr, "scene"); RNA_string_get(&scn_ptr, "name", scene_name); - + WM_operator_properties_create_ptr(&op_ptr, ot); RNA_string_set(&op_ptr, "layer", layer_name); RNA_string_set(&op_ptr, "scene", scene_name); diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c index 52b0292b9a3..f0567924edd 100644 --- a/source/blender/editors/space_node/node_buttons.c +++ b/source/blender/editors/space_node/node_buttons.c @@ -222,7 +222,7 @@ static int node_properties_poll(bContext *C) void NODE_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; - ot->description = "Toggles the properties panel display"; + ot->description = "Toggle the properties region visibility"; ot->idname = "NODE_OT_properties"; ot->exec = node_properties_toggle_exec; diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c index 5c58e9b720c..26eeaa91dd0 100644 --- a/source/blender/editors/space_node/node_group.c +++ b/source/blender/editors/space_node/node_group.c @@ -255,7 +255,7 @@ static int node_group_ungroup(bNodeTree *ntree, bNode *gnode) bAction *waction; /* firstly, wgroup needs to temporary dummy action that can be destroyed, as it shares copies */ - waction = wgroup->adt->action = BKE_action_copy(wgroup->adt->action); + waction = wgroup->adt->action = BKE_action_copy(G.main, wgroup->adt->action); /* now perform the moving */ BKE_animdata_separate_by_basepath(&wgroup->id, &ntree->id, &anim_basepaths); @@ -794,22 +794,37 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, link->tosock = node_group_find_input_socket(gnode, iosock->identifier); } else if (fromselect) { - bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link->fromnode, link->fromsock); - bNodeSocket *output_sock; - - /* update the group node and interface node sockets, - * so the new interface socket can be linked. - */ - node_group_verify(ntree, gnode, (ID *)ngroup); - node_group_output_verify(ngroup, output_node, (ID *)ngroup); + /* First check whether the source of this link is already connected to an output. + * If yes, reuse that output instead of duplicating it. */ + bool connected = false; + bNodeLink *olink; + for (olink = ngroup->links.first; olink; olink = olink->next) { + if (olink->fromsock == link->fromsock && olink->tonode == output_node) { + bNodeSocket *output_sock = node_group_find_output_socket(gnode, olink->tosock->identifier); + link->fromnode = gnode; + link->fromsock = output_sock; + connected = true; + } + } - /* create new internal link */ - output_sock = node_group_output_find_socket(output_node, iosock->identifier); - nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock); - - /* redirect external link */ - link->fromnode = gnode; - link->fromsock = node_group_find_output_socket(gnode, iosock->identifier); + if (!connected) { + bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link->fromnode, link->fromsock); + bNodeSocket *output_sock; + + /* update the group node and interface node sockets, + * so the new interface socket can be linked. + */ + node_group_verify(ntree, gnode, (ID *)ngroup); + node_group_output_verify(ngroup, output_node, (ID *)ngroup); + + /* create new internal link */ + output_sock = node_group_output_find_socket(output_node, iosock->identifier); + nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock); + + /* redirect external link */ + link->fromnode = gnode; + link->fromsock = node_group_find_output_socket(gnode, iosock->identifier); + } } } diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index d7249897723..ea3869ef387 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -1726,7 +1726,7 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w float duration; bool redraw = false; - if (!snode || event->type != TIMER || iofsd->anim_timer != event->customdata) + if (!snode || event->type != TIMER || iofsd == NULL || iofsd->anim_timer != event->customdata) return OPERATOR_PASS_THROUGH; duration = (float)iofsd->anim_timer->duration; diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 43e9c262172..b57462df53b 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -402,7 +402,7 @@ void restrictbutton_gr_restrict_flag(void *poin, void *poin2, int flag) if (group_restrict_flag(gr, flag)) { for (gob = gr->gobject.first; gob; gob = gob->next) { - if (gob->ob->id.lib) + if (ID_IS_LINKED_DATABLOCK(gob->ob)) continue; gob->ob->restrictflag &= ~flag; @@ -414,7 +414,7 @@ void restrictbutton_gr_restrict_flag(void *poin, void *poin2, int flag) } else { for (gob = gr->gobject.first; gob; gob = gob->next) { - if (gob->ob->id.lib) + if (ID_IS_LINKED_DATABLOCK(gob->ob)) continue; /* not in editmode */ @@ -655,7 +655,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, Scene *scene, ARegion *ar int but_flag = UI_BUT_DRAG_LOCK; gr = (Group *)tselem->id; - if (gr->id.lib) + if (ID_IS_LINKED_DATABLOCK(gr)) but_flag |= UI_BUT_DISABLED; UI_block_emboss_set(block, UI_EMBOSS_NONE); @@ -828,7 +828,7 @@ static void outliner_draw_userbuts(uiBlock *block, ARegion *ar, SpaceOops *soops char buf[16] = ""; int but_flag = UI_BUT_DRAG_LOCK; - if (id->lib) + if (ID_IS_LINKED_DATABLOCK(id)) but_flag |= UI_BUT_DISABLED; UI_block_emboss_set(block, UI_EMBOSS_NONE); @@ -993,7 +993,8 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon) } else { uiBut *but = uiDefIconBut(arg->block, UI_BTYPE_LABEL, 0, icon, arg->xb, arg->yb, UI_UNIT_X, UI_UNIT_Y, NULL, - 0.0, 0.0, 1.0, arg->alpha, (arg->id && arg->id->lib) ? arg->id->lib->name : ""); + 0.0, 0.0, 1.0, arg->alpha, + (arg->id && ID_IS_LINKED_DATABLOCK(arg->id)) ? arg->id->lib->name : ""); if (arg->id) UI_but_drag_set_id(but, arg->id); @@ -1001,7 +1002,7 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon) } -static void tselem_draw_gp_icon_uibut(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl) +static void UNUSED_FUNCTION(tselem_draw_gp_icon_uibut)(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl) { /* restrict column clip - skip it for now... */ if (arg->x >= arg->xmax) { @@ -1232,113 +1233,116 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto else UI_icon_draw(x, y, RNA_struct_ui_icon(te->rnaptr.type)); break; + /* Removed the icons from outliner. Need a better structure with Layers, Palettes and Colors */ +#if 0 case TSE_GP_LAYER: tselem_draw_gp_icon_uibut(&arg, tselem->id, te->directdata); break; +#endif default: UI_icon_draw(x, y, ICON_DOT); break; } } - else if (GS(tselem->id->name) == ID_OB) { - Object *ob = (Object *)tselem->id; - switch (ob->type) { - case OB_LAMP: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LAMP); break; - case OB_MESH: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_MESH); break; - case OB_CAMERA: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CAMERA); break; - case OB_CURVE: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CURVE); break; - case OB_MBALL: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_META); break; - case OB_LATTICE: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LATTICE); break; - case OB_ARMATURE: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_ARMATURE); break; - case OB_FONT: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_FONT); break; - case OB_SURF: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_SURFACE); break; - case OB_SPEAKER: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_SPEAKER); break; - case OB_EMPTY: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_EMPTY); break; - + else if (tselem->id) { + if (GS(tselem->id->name) == ID_OB) { + Object *ob = (Object *)tselem->id; + switch (ob->type) { + case OB_LAMP: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LAMP); break; + case OB_MESH: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_MESH); break; + case OB_CAMERA: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CAMERA); break; + case OB_CURVE: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CURVE); break; + case OB_MBALL: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_META); break; + case OB_LATTICE: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LATTICE); break; + case OB_ARMATURE: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_ARMATURE); break; + case OB_FONT: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_FONT); break; + case OB_SURF: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_SURFACE); break; + case OB_SPEAKER: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_SPEAKER); break; + case OB_EMPTY: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_EMPTY); break; + } } - } - else { - switch (GS(tselem->id->name)) { - case ID_SCE: - tselem_draw_icon_uibut(&arg, ICON_SCENE_DATA); break; - case ID_ME: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_MESH); break; - case ID_CU: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_CURVE); break; - case ID_MB: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_META); break; - case ID_LT: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_LATTICE); break; - case ID_LA: - { - Lamp *la = (Lamp *)tselem->id; - - switch (la->type) { - case LA_LOCAL: - tselem_draw_icon_uibut(&arg, ICON_LAMP_POINT); break; - case LA_SUN: - tselem_draw_icon_uibut(&arg, ICON_LAMP_SUN); break; - case LA_SPOT: - tselem_draw_icon_uibut(&arg, ICON_LAMP_SPOT); break; - case LA_HEMI: - tselem_draw_icon_uibut(&arg, ICON_LAMP_HEMI); break; - case LA_AREA: - tselem_draw_icon_uibut(&arg, ICON_LAMP_AREA); break; - default: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_LAMP); break; + else { + switch (GS(tselem->id->name)) { + case ID_SCE: + tselem_draw_icon_uibut(&arg, ICON_SCENE_DATA); break; + case ID_ME: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_MESH); break; + case ID_CU: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_CURVE); break; + case ID_MB: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_META); break; + case ID_LT: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_LATTICE); break; + case ID_LA: + { + Lamp *la = (Lamp *)tselem->id; + switch (la->type) { + case LA_LOCAL: + tselem_draw_icon_uibut(&arg, ICON_LAMP_POINT); break; + case LA_SUN: + tselem_draw_icon_uibut(&arg, ICON_LAMP_SUN); break; + case LA_SPOT: + tselem_draw_icon_uibut(&arg, ICON_LAMP_SPOT); break; + case LA_HEMI: + tselem_draw_icon_uibut(&arg, ICON_LAMP_HEMI); break; + case LA_AREA: + tselem_draw_icon_uibut(&arg, ICON_LAMP_AREA); break; + default: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_LAMP); break; + } + break; } - break; + case ID_MA: + tselem_draw_icon_uibut(&arg, ICON_MATERIAL_DATA); break; + case ID_TE: + tselem_draw_icon_uibut(&arg, ICON_TEXTURE_DATA); break; + case ID_IM: + tselem_draw_icon_uibut(&arg, ICON_IMAGE_DATA); break; + case ID_SPK: + case ID_SO: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_SPEAKER); break; + case ID_AR: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_ARMATURE); break; + case ID_CA: + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_CAMERA); break; + case ID_KE: + tselem_draw_icon_uibut(&arg, ICON_SHAPEKEY_DATA); break; + case ID_WO: + tselem_draw_icon_uibut(&arg, ICON_WORLD_DATA); break; + case ID_AC: + tselem_draw_icon_uibut(&arg, ICON_ACTION); break; + case ID_NLA: + tselem_draw_icon_uibut(&arg, ICON_NLA); break; + case ID_TXT: + tselem_draw_icon_uibut(&arg, ICON_SCRIPT); break; + case ID_GR: + tselem_draw_icon_uibut(&arg, ICON_GROUP); break; + case ID_LI: + if (tselem->id->tag & LIB_TAG_MISSING) { + tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_BROKEN); + } + else if (((Library *)tselem->id)->parent) { + tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_INDIRECT); + } + else { + tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_DIRECT); + } + break; + case ID_LS: + tselem_draw_icon_uibut(&arg, ICON_LINE_DATA); break; + case ID_GD: + tselem_draw_icon_uibut(&arg, ICON_GREASEPENCIL); break; } - case ID_MA: - tselem_draw_icon_uibut(&arg, ICON_MATERIAL_DATA); break; - case ID_TE: - tselem_draw_icon_uibut(&arg, ICON_TEXTURE_DATA); break; - case ID_IM: - tselem_draw_icon_uibut(&arg, ICON_IMAGE_DATA); break; - case ID_SPK: - case ID_SO: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_SPEAKER); break; - case ID_AR: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_ARMATURE); break; - case ID_CA: - tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_CAMERA); break; - case ID_KE: - tselem_draw_icon_uibut(&arg, ICON_SHAPEKEY_DATA); break; - case ID_WO: - tselem_draw_icon_uibut(&arg, ICON_WORLD_DATA); break; - case ID_AC: - tselem_draw_icon_uibut(&arg, ICON_ACTION); break; - case ID_NLA: - tselem_draw_icon_uibut(&arg, ICON_NLA); break; - case ID_TXT: - tselem_draw_icon_uibut(&arg, ICON_SCRIPT); break; - case ID_GR: - tselem_draw_icon_uibut(&arg, ICON_GROUP); break; - case ID_LI: - if (tselem->id->tag & LIB_TAG_MISSING) { - tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_BROKEN); - } - else if (((Library *)tselem->id)->parent) { - tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_INDIRECT); - } - else { - tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_DIRECT); - } - break; - case ID_LS: - tselem_draw_icon_uibut(&arg, ICON_LINE_DATA); break; - case ID_GD: - tselem_draw_icon_uibut(&arg, ICON_GREASEPENCIL); break; } } } @@ -1567,7 +1571,7 @@ static void outliner_draw_tree_element( else offsx += 2 * ufac; - if (tselem->type == 0 && tselem->id->lib) { + if (tselem->type == 0 && ID_IS_LINKED_DATABLOCK(tselem->id)) { glPixelTransferf(GL_ALPHA_SCALE, 0.5f); if (tselem->id->tag & LIB_TAG_MISSING) { UI_icon_draw((float)startx + offsx, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_BROKEN); diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 07608b59d2e..3c47f542dae 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -232,7 +232,7 @@ static void do_item_rename(ARegion *ar, TreeElement *te, TreeStoreElem *tselem, else if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) { BKE_report(reports, RPT_WARNING, "Cannot edit sequence name"); } - else if (tselem->id->lib) { + else if (ID_IS_LINKED_DATABLOCK(tselem->id)) { BKE_report(reports, RPT_WARNING, "Cannot edit external libdata"); } else if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) { @@ -417,13 +417,13 @@ static int outliner_id_remap_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (!(old_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) { + if (!(old_id && new_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) { BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT, "Invalid old/new ID pair ('%s' / '%s')", - old_id->name, new_id->name); + old_id ? old_id->name : "Invalid ID", new_id ? new_id->name : "Invalid ID"); return OPERATOR_CANCELLED; } - if (old_id->lib) { + if (ID_IS_LINKED_DATABLOCK(old_id)) { BKE_reportf(op->reports, RPT_WARNING, "Old ID '%s' is linked from a library, indirect usages of this datablock will not be remapped", old_id->name); @@ -1982,7 +1982,7 @@ static int parent_drop_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "child", childname); ob = (Object *)BKE_libblock_find_name(ID_OB, childname); - if (ob->id.lib) { + if (ID_IS_LINKED_DATABLOCK(ob)) { BKE_report(op->reports, RPT_INFO, "Can't edit library linked object"); return OPERATOR_CANCELLED; } @@ -2030,7 +2030,7 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (ob == par) { return OPERATOR_CANCELLED; } - if (ob->id.lib) { + if (ID_IS_LINKED_DATABLOCK(ob)) { BKE_report(op->reports, RPT_INFO, "Can't edit library linked object"); return OPERATOR_CANCELLED; } @@ -2239,7 +2239,7 @@ static int scene_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) RNA_string_get(op->ptr, "object", obname); ob = (Object *)BKE_libblock_find_name(ID_OB, obname); - if (ELEM(NULL, ob, scene) || scene->id.lib != NULL) { + if (ELEM(NULL, ob, scene) || ID_IS_LINKED_DATABLOCK(scene)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index bfec62997e1..e38886abeac 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -435,11 +435,11 @@ static void id_local_cb( bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) { - if (tselem->id->lib && (tselem->id->tag & LIB_TAG_EXTERN)) { + if (ID_IS_LINKED_DATABLOCK(tselem->id) && (tselem->id->tag & LIB_TAG_EXTERN)) { + Main *bmain = CTX_data_main(C); /* if the ID type has no special local function, * just clear the lib */ - if (id_make_local(tselem->id, false) == false) { - Main *bmain = CTX_data_main(C); + if (id_make_local(bmain, tselem->id, false, false) == false) { id_clear_lib_data(bmain, tselem->id); } } @@ -516,23 +516,16 @@ static void group_linkobs2scene_cb( Group *group = (Group *)tselem->id; GroupObject *gob; Base *base; - + for (gob = group->gobject.first; gob; gob = gob->next) { base = BKE_scene_base_find(scene, gob->ob); - if (base) { - base->object->flag |= SELECT; - base->flag |= SELECT; - } - else { + if (!base) { /* link to scene */ - base = MEM_callocN(sizeof(Base), "add_base"); - BLI_addhead(&scene->base, base); - base->lay = gob->ob->lay; - gob->ob->flag |= SELECT; - base->flag = gob->ob->flag; - base->object = gob->ob; + base = BKE_scene_base_add(scene, gob->ob); id_lib_extern((ID *)gob->ob); /* in case these are from a linked group */ } + base->object->flag |= SELECT; + base->flag |= SELECT; } } @@ -1352,7 +1345,7 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); SpaceOops *soops = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - eOutlinerIdOpTypes event; + eOutlinerLibOpTypes event; /* check for invalid states */ if (soops == NULL) diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index a687f61d69f..b22e6595caf 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -490,7 +490,7 @@ static void outliner_add_object_contents(SpaceOops *soops, TreeElement *te, Tree outliner_add_element(soops, &te->subtree, ob->poselib, te, 0, 0); // XXX FIXME.. add a special type for this - if (ob->proxy && ob->id.lib == NULL) + if (ob->proxy && !ID_IS_LINKED_DATABLOCK(ob)) outliner_add_element(soops, &te->subtree, ob->proxy, te, TSE_PROXY, 0); outliner_add_element(soops, &te->subtree, ob->gpd, te, 0, 0); diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index c11a4d2e5a3..ede6b7ce469 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -1071,7 +1071,7 @@ static int sequencer_add_effect_strip_invoke(bContext *C, wmOperator *op, const { bool is_type_set = RNA_struct_property_is_set(op->ptr, "type"); int type = -1; - int prop_flag = SEQPROP_ENDFRAME; + int prop_flag = SEQPROP_ENDFRAME | SEQPROP_NOPATHS; if (is_type_set) { type = RNA_enum_get(op->ptr, "type"); @@ -1106,9 +1106,6 @@ void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - WM_operator_properties_filesel( - ot, 0, FILE_SPECIAL, FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME); RNA_def_enum(ot->srna, "type", sequencer_prop_effect_types, SEQ_TYPE_CROSS, "Type", "Sequencer effect type"); RNA_def_float_vector(ot->srna, "color", 3, NULL, 0.0f, 1.0f, "Color", diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index 86d3fcbe1ac..b33a26db728 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -94,7 +94,7 @@ void SEQUENCER_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "SEQUENCER_OT_properties"; - ot->description = "Open sequencer properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = sequencer_properties_toggle_exec; ot->poll = ED_operator_sequencer_active; diff --git a/source/blender/editors/space_text/text_header.c b/source/blender/editors/space_text/text_header.c index 91665f1a598..e06a5b5474e 100644 --- a/source/blender/editors/space_text/text_header.c +++ b/source/blender/editors/space_text/text_header.c @@ -92,7 +92,7 @@ void TEXT_OT_properties(wmOperatorType *ot) { /* identifiers */ ot->name = "Properties"; - ot->description = "Toggle text properties panel"; + ot->description = "Toggle the properties region visibility"; ot->idname = "TEXT_OT_properties"; /* api callbacks */ diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 94ed280f792..e7428fd79d9 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -92,7 +92,7 @@ static int text_edit_poll(bContext *C) if (!text) return 0; - if (text->id.lib) { + if (ID_IS_LINKED_DATABLOCK(text)) { // BKE_report(op->reports, RPT_ERROR, "Cannot edit external libdata"); return 0; } @@ -108,7 +108,7 @@ int text_space_edit_poll(bContext *C) if (!st || !text) return 0; - if (text->id.lib) { + if (ID_IS_LINKED_DATABLOCK(text)) { // BKE_report(op->reports, RPT_ERROR, "Cannot edit external libdata"); return 0; } @@ -128,7 +128,7 @@ static int text_region_edit_poll(bContext *C) if (!ar || ar->regiontype != RGN_TYPE_WINDOW) return 0; - if (text->id.lib) { + if (ID_IS_LINKED_DATABLOCK(text)) { // BKE_report(op->reports, RPT_ERROR, "Cannot edit external libdata"); return 0; } diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index fde955c24a7..525d42a1965 100644 --- a/source/blender/editors/space_time/space_time.c +++ b/source/blender/editors/space_time/space_time.c @@ -585,6 +585,10 @@ static void time_main_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), break; } break; + case NC_GPENCIL: + if (wmn->data == ND_DATA) + ED_region_tag_redraw(ar); + break; } } diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index 755d4985518..614289997c1 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -237,7 +237,9 @@ static struct TextureDrawState { bool texpaint_material; /* use material slots for texture painting */ } Gtexdraw = {NULL, NULL, NULL, false, 0, 0, 0, false, false, {0, 0, 0, 0}, false, false}; -static bool set_draw_settings_cached(int clearcache, MTexPoly *texface, Material *ma, struct TextureDrawState gtexdraw) +static bool set_draw_settings_cached( + int clearcache, MTexPoly *texface, Material *ma, + const struct TextureDrawState *gtexdraw) { static Material *c_ma; static int c_textured; @@ -253,7 +255,7 @@ static bool set_draw_settings_cached(int clearcache, MTexPoly *texface, Material int lit = 0; int has_texface = texface != NULL; bool need_set_tpage = false; - bool texpaint = ((gtexdraw.ob->mode & OB_MODE_TEXTURE_PAINT) != 0); + bool texpaint = ((gtexdraw->ob->mode & OB_MODE_TEXTURE_PAINT) != 0); Image *ima = NULL; @@ -271,16 +273,18 @@ static bool set_draw_settings_cached(int clearcache, MTexPoly *texface, Material c_ma = NULL; } else { - textured = gtexdraw.is_tex; + textured = gtexdraw->is_tex; } /* convert number of lights into boolean */ - if (gtexdraw.is_lit) lit = 1; + if (gtexdraw->is_lit) { + lit = 1; + } - backculled = gtexdraw.use_backface_culling; + backculled = gtexdraw->use_backface_culling; if (ma) { if (ma->mode & MA_SHLESS) lit = 0; - if (gtexdraw.use_game_mat) { + if (gtexdraw->use_game_mat) { backculled = backculled || (ma->game.flag & GEMAT_BACKCULL); alphablend = ma->game.alpha_blend; } @@ -294,10 +298,10 @@ static bool set_draw_settings_cached(int clearcache, MTexPoly *texface, Material alphablend = GPU_BLEND_ALPHA; } else if (texpaint) { - if (gtexdraw.texpaint_material) + if (gtexdraw->texpaint_material) ima = ma && ma->texpaintslot ? ma->texpaintslot[ma->paint_active_slot].ima : NULL; else - ima = gtexdraw.canvas; + ima = gtexdraw->canvas; } else textured = 0; @@ -375,7 +379,7 @@ static bool set_draw_settings_cached(int clearcache, MTexPoly *texface, Material if (c_textured && !c_badtex) { options |= GPU_SHADER_TEXTURE_2D; } - if (gtexdraw.two_sided_lighting) { + if (gtexdraw->two_sided_lighting) { options |= GPU_SHADER_TWO_SIDED; } @@ -431,12 +435,16 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O else { /* draw with lights in the scene otherwise */ solidtex = false; - if (v3d->flag2 & V3D_SHADELESS_TEX) + if (v3d->flag2 & V3D_SHADELESS_TEX) { Gtexdraw.is_lit = 0; - else - Gtexdraw.is_lit = GPU_scene_object_lights(scene, ob, v3d->lay, rv3d->viewmat, !rv3d->is_persp); + } + else { + Gtexdraw.is_lit = GPU_scene_object_lights( + scene, ob, v3d->localvd ? v3d->localvd->lay : v3d->lay, + rv3d->viewmat, !rv3d->is_persp); + } } - + rgba_float_to_uchar(obcol, ob->col); if (solidtex || v3d->drawtype == OB_TEXTURE) is_tex = true; @@ -495,7 +503,7 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O Gtexdraw.two_sided_lighting = (me->flag & ME_TWOSIDED); memcpy(Gtexdraw.obcol, obcol, sizeof(obcol)); - set_draw_settings_cached(1, NULL, NULL, Gtexdraw); + set_draw_settings_cached(1, NULL, NULL, &Gtexdraw); glCullFace(GL_BACK); } @@ -553,7 +561,7 @@ static DMDrawOption draw_tface__set_draw_legacy(MTexPoly *mtexpoly, const bool h if (ma && (ma->game.flag & GEMAT_INVISIBLE)) return DM_DRAW_OPTION_SKIP; - invalidtexture = set_draw_settings_cached(0, mtexpoly, ma, Gtexdraw); + invalidtexture = set_draw_settings_cached(0, mtexpoly, ma, &Gtexdraw); if (mtexpoly && invalidtexture) { glColor3ub(0xFF, 0x00, 0xFF); @@ -594,7 +602,7 @@ static DMDrawOption draw_tface__set_draw(MTexPoly *mtexpoly, const bool UNUSED(h if (ma && (ma->game.flag & GEMAT_INVISIBLE)) return DM_DRAW_OPTION_SKIP; if (mtexpoly || Gtexdraw.is_texpaint) - set_draw_settings_cached(0, mtexpoly, ma, Gtexdraw); + set_draw_settings_cached(0, mtexpoly, ma, &Gtexdraw); /* always use color from mcol, as set in update_tface_color_layer */ return DM_DRAW_OPTION_NORMAL; @@ -664,7 +672,7 @@ static void update_tface_color_layer(DerivedMesh *dm, bool use_mcol) copy_mode = COPY_PREV; } } - else if (mtexpoly && set_draw_settings_cached(0, mtexpoly, ma, Gtexdraw)) { + else if (mtexpoly && set_draw_settings_cached(0, mtexpoly, ma, &Gtexdraw)) { int loop_index = mp->loopstart; for (j = 0; j < mp->totloop; j++, loop_index++) { finalCol[loop_index].r = 255; @@ -830,7 +838,7 @@ static void draw_mesh_text(Scene *scene, Object *ob, int glsl) } } else { - badtex = set_draw_settings_cached(0, mtpoly, mat, Gtexdraw); + badtex = set_draw_settings_cached(0, mtpoly, mat, &Gtexdraw); if (badtex) { continue; } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index acc20997b27..7d281a344f3 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -4671,7 +4671,7 @@ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3 index3_nors_incr = true; } else { - if (!render_only || (render_only && BKE_displist_has_faces(lb))) { + if (!render_only || BKE_displist_has_faces(lb)) { return drawDispListwire(lb, ob->type); } } @@ -7342,7 +7342,7 @@ static void draw_object_wire_color(Scene *scene, Base *base, unsigned char r_ob_ } else { /* Sets the 'colindex' */ - if (ob->id.lib) { + if (ID_IS_LINKED_DATABLOCK(ob)) { colindex = (base->flag & (SELECT + BA_WAS_SEL)) ? 2 : 1; } /* Sets the 'theme_id' or fallback to wire */ @@ -8098,7 +8098,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short !(G.f & G_RENDER_OGL)) { /* check > 0 otherwise grease pencil can draw into the circle select which is annoying. */ - drawcentercircle(v3d, rv3d, ob->obmat[3], do_draw_center, ob->id.lib || ob->id.us > 1); + drawcentercircle(v3d, rv3d, ob->obmat[3], do_draw_center, ID_IS_LINKED_DATABLOCK(ob) || ob->id.us > 1); } } } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 0805b81147f..8139a407a43 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1486,7 +1486,9 @@ static void view3d_id_remap(ScrArea *sa, SpaceLink *slink, ID *old_id, ID *new_i if ((ID *)v3d->camera == old_id) { v3d->camera = (Object *)new_id; if (!new_id) { - for (ar = sa->regionbase.first; ar; ar = ar->next) { + /* 3D view might be inactive, in that case needs to use slink->regionbase */ + ListBase *regionbase = (slink == sa->spacedata.first) ? &sa->regionbase : &slink->regionbase; + for (ar = regionbase->first; ar; ar = ar->next) { if (ar->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = is_local ? ((RegionView3D *)ar->regiondata)->localvd : ar->regiondata; if (rv3d && (rv3d->persp == RV3D_CAMOB)) { diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index c52d1238163..b9c8c98b62f 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1206,7 +1206,7 @@ static int view3d_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op)) void VIEW3D_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; - ot->description = "Toggles the properties panel display"; + ot->description = "Toggle the properties region visibility"; ot->idname = "VIEW3D_OT_properties"; ot->exec = view3d_properties_toggle_exec; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 62059766bd6..6463c137c2b 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -2607,6 +2607,7 @@ static void gpu_update_lamps_shadows_world(Scene *scene, View3D *v3d) GPU_mist_update_values(world->mistype, world->miststa, world->mistdist, world->misi, &world->horr); GPU_horizon_update_color(&world->horr); GPU_ambient_update_color(&world->ambr); + GPU_zenith_update_color(&world->zenr); } } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 1bd0ec23d65..215d90a8878 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -110,7 +110,7 @@ static bool view3d_operator_offset_lock_check(bContext *C, wmOperator *op) bool ED_view3d_camera_lock_check(const View3D *v3d, const RegionView3D *rv3d) { return ((v3d->camera) && - (v3d->camera->id.lib == NULL) && + (!ID_IS_LINKED_DATABLOCK(v3d->camera)) && (v3d->flag2 & V3D_LOCK_CAMERA) && (rv3d->persp == RV3D_CAMOB)); } @@ -3079,7 +3079,9 @@ static int viewselected_exec(bContext *C, wmOperator *op) else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) { ok = PE_minmax(scene, min, max); } - else if (ob && (ob->mode & (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT))) { + else if (ob && + (ob->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) + { BKE_paint_stroke_get_average(scene, ob, min); copy_v3_v3(max, min); ok = true; @@ -3434,12 +3436,8 @@ static int render_border_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); } - /* drawing a border surrounding the entire camera view switches off border rendering - * or the border covers no pixels */ - if ((border.xmin <= 0.0f && border.xmax >= 1.0f && - border.ymin <= 0.0f && border.ymax >= 1.0f) || - (border.xmin == border.xmax || border.ymin == border.ymax)) - { + /* drawing a border outside the camera view switches off border rendering */ + if ((border.xmin == border.xmax || border.ymin == border.ymax)) { if (rv3d->persp == RV3D_CAMOB) scene->r.mode &= ~R_BORDER; else @@ -4520,7 +4518,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot) /* note: having key shortcut here is bad practice, * but for now keep because this displays when dragging an image over the 3D viewport */ ot->name = "Add Background Image (Ctrl for Empty Object)"; - ot->description = "Add a new background image"; + ot->description = "Add a new background image (Ctrl for Empty Object)"; ot->idname = "VIEW3D_OT_background_image_add"; /* api callbacks */ @@ -4529,7 +4527,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot) ot->poll = ED_operator_view3d_active; /* flags */ - ot->flag = 0; + ot->flag = OPTYPE_UNDO; /* properties */ RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign"); diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index 8418cf0980c..31377d0fce8 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -346,7 +346,7 @@ static bool initFlyInfo(bContext *C, FlyInfo *fly, wmOperator *op, const wmEvent fly->rv3d->persp = RV3D_PERSP; } - if (fly->rv3d->persp == RV3D_CAMOB && fly->v3d->camera->id.lib) { + if (fly->rv3d->persp == RV3D_CAMOB && ID_IS_LINKED_DATABLOCK(fly->v3d->camera)) { BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library"); return false; } diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index 6d831c667a8..bdd2702a6ce 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -274,6 +274,7 @@ bool ED_view3d_minmax_verts(struct Object *obedit, float min[3], float max[3]); void VIEW3D_OT_snap_selected_to_grid(struct wmOperatorType *ot); void VIEW3D_OT_snap_selected_to_cursor(struct wmOperatorType *ot); +void VIEW3D_OT_snap_selected_to_active(struct wmOperatorType *ot); void VIEW3D_OT_snap_cursor_to_grid(struct wmOperatorType *ot); void VIEW3D_OT_snap_cursor_to_center(struct wmOperatorType *ot); void VIEW3D_OT_snap_cursor_to_selected(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index 1c84ce3c985..b273f46fca3 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -214,6 +214,7 @@ void view3d_operatortypes(void) WM_operatortype_append(VIEW3D_OT_snap_selected_to_grid); WM_operatortype_append(VIEW3D_OT_snap_selected_to_cursor); + WM_operatortype_append(VIEW3D_OT_snap_selected_to_active); WM_operatortype_append(VIEW3D_OT_snap_cursor_to_grid); WM_operatortype_append(VIEW3D_OT_snap_cursor_to_center); WM_operatortype_append(VIEW3D_OT_snap_cursor_to_selected); diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index ac05853e6d0..7448d4c658e 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -312,25 +312,9 @@ static void view3d_win_to_ray_segment( if (!r_ray_co) r_ray_co = _ray_co; if (!r_ray_dir) r_ray_dir = _ray_dir; + ED_view3d_win_to_origin(ar, mval, r_ray_co); ED_view3d_win_to_vector(ar, mval, r_ray_dir); - if (rv3d->is_persp) { - copy_v3_v3(r_ray_co, rv3d->viewinv[3]); - } - else { - r_ray_co[0] = 2.0f * mval[0] / ar->winx - 1.0f; - r_ray_co[1] = 2.0f * mval[1] / ar->winy - 1.0f; - - if (rv3d->persp == RV3D_CAMOB) { - r_ray_co[2] = -1.0f; - } - else { - r_ray_co[2] = 0.0f; - } - - mul_project_m4_v3(rv3d->persinv, r_ray_co); - } - if ((rv3d->is_persp == false) && (rv3d->persp != RV3D_CAMOB)) { end_offset = v3d->far / 2.0f; start_offset = -end_offset; @@ -347,7 +331,7 @@ static void view3d_win_to_ray_segment( } } -BLI_INLINE bool view3d_clip_segment(const RegionView3D *rv3d, float ray_start[3], float ray_end[3]) +bool ED_view3d_clip_segment(const RegionView3D *rv3d, float ray_start[3], float ray_end[3]) { if ((rv3d->rflag & RV3D_CLIPPING) && (clip_segment_v3_plane_n(ray_start, ray_end, rv3d->clip, 6, @@ -384,7 +368,7 @@ bool ED_view3d_win_to_ray_ex( /* bounds clipping */ if (do_clip) { - return view3d_clip_segment(ar->regiondata, r_ray_start, ray_end); + return ED_view3d_clip_segment(ar->regiondata, r_ray_start, ray_end); } return true; @@ -549,6 +533,37 @@ void ED_view3d_win_to_delta(const ARegion *ar, const float mval[2], float out[3] } /** + * Calculate a 3d origin from 2d window coordinates. + * \note Orthographic views have a less obvious origin, + * Since far clip can be a very large value resulting in numeric precision issues, + * the origin in this case is close to zero coordinate. + * + * \param ar The region (used for the window width and height). + * \param mval The area relative 2d location (such as event->mval converted to floats). + * \param out The resulting normalized world-space direction vector. + */ +void ED_view3d_win_to_origin(const ARegion *ar, const float mval[2], float out[3]) +{ + RegionView3D *rv3d = ar->regiondata; + if (rv3d->is_persp) { + copy_v3_v3(out, rv3d->viewinv[3]); + } + else { + out[0] = 2.0f * mval[0] / ar->winx - 1.0f; + out[1] = 2.0f * mval[1] / ar->winy - 1.0f; + + if (rv3d->persp == RV3D_CAMOB) { + out[2] = -1.0f; + } + else { + out[2] = 0.0f; + } + + mul_project_m4_v3(rv3d->persinv, out); + } +} + +/** * Calculate a 3d direction vector from 2d window coordinates. * This direction vector starts and the view in the direction of the 2d window coordinates. * In orthographic view all window coordinates yield the same vector. @@ -599,7 +614,7 @@ bool ED_view3d_win_to_segment(const ARegion *ar, View3D *v3d, const float mval[2 /* bounds clipping */ if (do_clip) { - return view3d_clip_segment((RegionView3D *)ar->regiondata, r_ray_start, r_ray_end); + return ED_view3d_clip_segment((RegionView3D *)ar->regiondata, r_ray_start, r_ray_end); } return true; diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index f46608b7d5e..a55849e5633 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -327,6 +327,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) for (j = 0; j < 3; j++) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; + pt->strength = 1.0f; pt++; } } @@ -336,6 +337,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) for (j = 0; j < 3; j += 2) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; + pt->strength = 1.0f; pt++; } } diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index e8e7d3c62fb..5dd69cc66eb 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -44,6 +44,7 @@ #include "BKE_main.h" #include "BKE_mball.h" #include "BKE_object.h" +#include "BKE_report.h" #include "BKE_tracking.h" #include "WM_api.h" @@ -204,7 +205,7 @@ void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot) /* *************************************************** */ -static int snap_sel_to_curs_exec(bContext *C, wmOperator *op) +static int snap_selected_to_location(bContext *C, const float snap_target_global[3], const bool use_offset) { Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); @@ -213,15 +214,10 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op) TransVertStore tvs = {NULL}; TransVert *tv; float imat[3][3], bmat[3][3]; - const float *cursor_global; float center_global[3]; float offset_global[3]; int a; - const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); - - cursor_global = ED_view3d_cursor3d_get(scene, v3d); - if (use_offset) { if ((v3d && v3d->around == V3D_AROUND_ACTIVE) && snap_calc_active_center(C, true, center_global)) @@ -231,11 +227,11 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op) else { snap_curs_to_sel_ex(C, center_global); } - sub_v3_v3v3(offset_global, cursor_global, center_global); + sub_v3_v3v3(offset_global, snap_target_global, center_global); } if (obedit) { - float cursor_local[3]; + float snap_target_local[3]; if (ED_transverts_check_obedit(obedit)) ED_transverts_create_from_obedit(&tvs, obedit, 0); @@ -246,8 +242,8 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op) invert_m3_m3(imat, bmat); /* get the cursor in object space */ - sub_v3_v3v3(cursor_local, cursor_global, obedit->obmat[3]); - mul_m3_v3(imat, cursor_local); + sub_v3_v3v3(snap_target_local, snap_target_global, obedit->obmat[3]); + mul_m3_v3(imat, snap_target_local); if (use_offset) { float offset_local[3]; @@ -262,7 +258,7 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op) else { tv = tvs.transverts; for (a = 0; a < tvs.transverts_tot; a++, tv++) { - copy_v3_v3(tv->loc, cursor_local); + copy_v3_v3(tv->loc, snap_target_local); } } @@ -274,10 +270,10 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op) bPoseChannel *pchan; bArmature *arm = obact->data; - float cursor_local[3]; + float snap_target_local[3]; invert_m4_m4(obact->imat, obact->obmat); - mul_v3_m4v3(cursor_local, obact->imat, cursor_global); + mul_v3_m4v3(snap_target_local, obact->imat, snap_target_global); for (pchan = obact->pose->chanbase.first; pchan; pchan = pchan->next) { if ((pchan->bone->flag & BONE_SELECTED) && @@ -311,7 +307,7 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op) BKE_armature_loc_pose_to_bone(pchan, cursor_pose, cursor_pose); } else { - BKE_armature_loc_pose_to_bone(pchan, cursor_local, cursor_pose); + BKE_armature_loc_pose_to_bone(pchan, snap_target_local, cursor_pose); } /* copy new position */ @@ -367,7 +363,7 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op) add_v3_v3v3(cursor_parent, ob->obmat[3], offset_global); } else { - copy_v3_v3(cursor_parent, cursor_global); + copy_v3_v3(cursor_parent, snap_target_global); } sub_v3_v3(cursor_parent, ob->obmat[3]); @@ -401,6 +397,18 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op) +{ + const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); + + Scene *scene = CTX_data_scene(C); + View3D *v3d = CTX_wm_view3d(C); + + const float *snap_target_global = ED_view3d_cursor3d_get(scene, v3d); + + return snap_selected_to_location(C, snap_target_global, use_offset); +} + void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot) { /* identifiers */ @@ -409,7 +417,7 @@ void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot) ot->idname = "VIEW3D_OT_snap_selected_to_cursor"; /* api callbacks */ - ot->exec = snap_sel_to_curs_exec; + ot->exec = snap_selected_to_cursor_exec; ot->poll = ED_operator_view3d_active; /* flags */ @@ -419,6 +427,34 @@ void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_offset", 1, "Offset", ""); } +static int snap_selected_to_active_exec(bContext *C, wmOperator *op) +{ + float snap_target_global[3]; + + if (snap_calc_active_center(C, false, snap_target_global) == false) { + BKE_report(op->reports, RPT_ERROR, "No active element found!"); + return OPERATOR_CANCELLED; + } + + return snap_selected_to_location(C, snap_target_global, false); +} + +void VIEW3D_OT_snap_selected_to_active(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Snap Selection to Active"; + ot->description = "Snap selected item(s) to the active item"; + ot->idname = "VIEW3D_OT_snap_selected_to_active"; + + /* api callbacks */ + ot->exec = snap_selected_to_active_exec; + ot->poll = ED_operator_view3d_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + /* *************************************************** */ static int snap_curs_to_grid_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index c35646b9e92..bc7a9989c72 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -221,7 +221,7 @@ void ED_view3d_smooth_view_ex( copy_v3_v3(sms.dyn_ofs, sview->dyn_ofs); sms.use_dyn_ofs = true; - /* calcualte the final destination offset */ + /* calculate the final destination offset */ view3d_orbit_apply_dyn_ofs(sms.dst.ofs, sms.src.ofs, sms.src.quat, sms.dst.quat, sms.dyn_ofs); } @@ -507,7 +507,7 @@ static int view3d_camera_to_view_poll(bContext *C) if (ED_view3d_context_user_region(C, &v3d, &ar)) { RegionView3D *rv3d = ar->regiondata; - if (v3d && v3d->camera && v3d->camera->id.lib == NULL) { + if (v3d && v3d->camera && !ID_IS_LINKED_DATABLOCK(v3d->camera)) { if (rv3d && (rv3d->viewlock & RV3D_LOCKED) == 0) { if (rv3d->persp != RV3D_CAMOB) { return 1; diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 384da277612..c9e4bb301b8 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -505,7 +505,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->rv3d->persp = RV3D_PERSP; } - if (walk->rv3d->persp == RV3D_CAMOB && walk->v3d->camera->id.lib) { + if (walk->rv3d->persp == RV3D_CAMOB && ID_IS_LINKED_DATABLOCK(walk->v3d->camera)) { BKE_report(op->reports, RPT_ERROR, "Cannot navigate a camera from an external library"); return false; } @@ -924,8 +924,7 @@ static void walkEvent(bContext *C, wmOperator *op, WalkInfo *walk, const wmEvent copy_v3_v3(teleport->origin, walk->rv3d->viewinv[3]); /* stop the camera from a distance (camera height) */ - normalize_v3(nor); - mul_v3_fl(nor, walk->view_height); + normalize_v3_length(nor, walk->view_height); add_v3_v3(loc, nor); sub_v3_v3v3(teleport->direction, loc, teleport->origin); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 95864474fc0..ef6cff19181 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -51,6 +51,7 @@ #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_ghash.h" +#include "BLI_stackdefines.h" #include "BLI_memarena.h" #include "BKE_nla.h" @@ -100,7 +101,6 @@ static void doVertSlide(TransInfo *t, float perc); static void drawEdgeSlide(TransInfo *t); static void drawVertSlide(TransInfo *t); -static void len_v3_ensure(float v[3], const float length); static void postInputRotation(TransInfo *t, float values[3]); static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short around); @@ -3720,19 +3720,10 @@ static void initRotation(TransInfo *t) copy_v3_v3(t->axis_orig, t->axis); } -static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short around) +static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], const float *center) { float vec[3], totmat[3][3], smat[3][3]; float eul[3], fmat[3][3], quat[4]; - const float *center; - - /* local constraint shouldn't alter center */ - if (transdata_check_local_center(t, around)) { - center = td->center; - } - else { - center = t->center; - } if (t->flag & T_POINTS) { mul_m3_m3m3(totmat, mat, td->mtx); @@ -3942,6 +3933,21 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short } } +static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around) +{ + const float *center; + + /* local constraint shouldn't alter center */ + if (transdata_check_local_center(t, around)) { + center = td->center; + } + else { + center = t->center; + } + + ElementRotation_ex(t, td, mat, center); +} + static void applyRotationValue(TransInfo *t, float angle, float axis[3]) { TransData *td = t->data; @@ -3991,10 +3997,8 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) applySnapping(t, &final); - if (applyNumInput(&t->num, &final)) { - /* Clamp between -PI and PI */ - final = angle_wrap_rad(final); - } + /* Used to clamp final result in [-PI, PI[ range, no idea why, inheritance from 2.4x area, see T48998. */ + applyNumInput(&t->num, &final); t->values[0] = final; @@ -4342,12 +4346,16 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) if (td->flag & TD_SKIP) continue; + float rotate_offset[3] = {0}; + bool use_rotate_offset = false; + /* handle snapping rotation before doing the translation */ if (usingSnappingNormal(t)) { + float mat[3][3]; + if (validSnappingNormal(t)) { const float *original_normal; - float mat[3][3]; - + /* In pose mode, we want to align normals with Y axis of bones... */ if (t->flag & T_POSE) original_normal = td->axismtx[1]; @@ -4355,18 +4363,19 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) original_normal = td->axismtx[2]; rotation_between_vecs_to_mat3(mat, original_normal, t->tsnap.snapNormal); - - ElementRotation(t, td, mat, V3D_AROUND_LOCAL_ORIGINS); } else { - float mat[3][3]; - unit_m3(mat); - - ElementRotation(t, td, mat, V3D_AROUND_LOCAL_ORIGINS); + } + + ElementRotation_ex(t, td, mat, t->tsnap.snapTarget); + + if (td->loc) { + use_rotate_offset = true; + sub_v3_v3v3(rotate_offset, td->loc, td->iloc); } } - + if (t->con.applyVec) { float pvec[3]; t->con.applyVec(t, td, vec, tvec, pvec); @@ -4374,6 +4383,10 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) else { copy_v3_v3(tvec, vec); } + + if (use_rotate_offset) { + add_v3_v3(tvec, rotate_offset); + } mul_m3_v3(td->smtx, tvec); mul_v3_fl(tvec, td->factor); @@ -4382,7 +4395,7 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) if (td->loc) add_v3_v3v3(td->loc, td->iloc, tvec); - + constraintTransLim(t, td); } } @@ -4957,9 +4970,7 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) project_v3_v3v3(vec, vec, axis); } } - normalize_v3(vec); - mul_v3_fl(vec, distance); - mul_v3_fl(vec, td->factor); + normalize_v3_length(vec, distance * td->factor); add_v3_v3v3(td->loc, td->iloc, vec); } @@ -5753,13 +5764,6 @@ static void interp_line_v3_v3v3v3(float p[3], const float v1[3], const float v2[ } } - -static void len_v3_ensure(float v[3], const float length) -{ - normalize_v3(v); - mul_v3_fl(v, length); -} - /** * Find the closest point on the ngon on the opposite side. * used to set the edge slide distance for ngons. @@ -5823,7 +5827,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l, if (l->e == e_next) { if (i) { - len_v3_ensure(vec_accum, vec_accum_len / (float)i); + normalize_v3_length(vec_accum, vec_accum_len / (float)i); } else { /* When there is no edge to slide along, @@ -5843,7 +5847,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l, cross_v3_v3v3(vec_accum, l_tmp->f->no, tdir); #if 0 /* rough guess, we can do better! */ - len_v3_ensure(vec_accum, (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f); + normalize_v3_length(vec_accum, (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f); #else /* be clever, check the opposite ngon edge to slide into. * this gives best results */ @@ -5858,7 +5862,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l, dist = (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f; } - len_v3_ensure(vec_accum, dist); + normalize_v3_length(vec_accum, dist); } #endif } @@ -5879,7 +5883,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l, if (BM_loop_other_edge_loop(l, v)->e == e_next) { if (i) { - len_v3_ensure(vec_accum, vec_accum_len / (float)i); + normalize_v3_length(vec_accum, vec_accum_len / (float)i); } copy_v3_v3(r_slide_vec, vec_accum); @@ -5890,7 +5894,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l, ((l = l->radial_next) != l_first)); if (i) { - len_v3_ensure(vec_accum, vec_accum_len / (float)i); + normalize_v3_length(vec_accum, vec_accum_len / (float)i); } copy_v3_v3(r_slide_vec, vec_accum); @@ -5903,7 +5907,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l, */ static void calcEdgeSlide_mval_range( TransInfo *t, EdgeSlideData *sld, const int *sv_table, const int loop_nr, - const float mval[2], const bool use_btree_disp, const bool use_calc_direction) + const float mval[2], const bool use_occlude_geometry, const bool use_calc_direction) { TransDataEdgeSlideVert *sv_array = sld->sv; BMEditMesh *em = BKE_editmesh_from_object(t->obedit); @@ -5912,7 +5916,7 @@ static void calcEdgeSlide_mval_range( View3D *v3d = NULL; RegionView3D *rv3d = NULL; float projectMat[4][4]; - BMBVHTree *btree; + BMBVHTree *bmbvh; /* only for use_calc_direction */ float (*loop_dir)[3] = NULL, *loop_maxdist = NULL; @@ -5936,11 +5940,11 @@ static void calcEdgeSlide_mval_range( ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat); } - if (use_btree_disp) { - btree = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false); + if (use_occlude_geometry) { + bmbvh = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false); } else { - btree = NULL; + bmbvh = NULL; } /* find mouse vectors, the global one, and one per loop in case we have @@ -5975,7 +5979,7 @@ static void calcEdgeSlide_mval_range( continue; /* This test is only relevant if object is not wire-drawn! See [#32068]. */ - if (use_btree_disp && !BMBVH_EdgeVisible(btree, e_other, ar, v3d, t->obedit)) { + if (use_occlude_geometry && !BMBVH_EdgeVisible(bmbvh, e_other, ar, v3d, t->obedit)) { continue; } @@ -6056,8 +6060,8 @@ static void calcEdgeSlide_mval_range( sld->mval_end[0] = t->mval[0] + mval_end[0]; sld->mval_end[1] = t->mval[1] + mval_end[1]; - if (btree) { - BKE_bmbvh_free(btree); + if (bmbvh) { + BKE_bmbvh_free(bmbvh); } } @@ -6119,8 +6123,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f int *sv_table; /* BMVert -> sv_array index */ EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); float mval[2] = {(float)t->mval[0], (float)t->mval[1]}; - int numsel, i, j, loop_nr; - bool use_btree_disp = false; + int numsel, i, loop_nr; + bool use_occlude_geometry = false; View3D *v3d = NULL; RegionView3D *rv3d = NULL; @@ -6167,31 +6171,39 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__); - j = 0; - BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { - if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { - BM_elem_flag_enable(v, BM_ELEM_TAG); - sv_table[i] = j; - j += 1; - } - else { - BM_elem_flag_disable(v, BM_ELEM_TAG); - sv_table[i] = -1; +#define INDEX_UNSET -1 +#define INDEX_INVALID -2 + + { + int j = 0; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + BM_elem_flag_enable(v, BM_ELEM_TAG); + sv_table[i] = INDEX_UNSET; + j += 1; + } + else { + BM_elem_flag_disable(v, BM_ELEM_TAG); + sv_table[i] = INDEX_INVALID; + } + BM_elem_index_set(v, i); /* set_inline */ } - BM_elem_index_set(v, i); /* set_inline */ - } - bm->elem_index_dirty &= ~BM_VERT; + bm->elem_index_dirty &= ~BM_VERT; - if (!j) { - MEM_freeN(sld); - MEM_freeN(sv_table); - return false; + if (!j) { + MEM_freeN(sld); + MEM_freeN(sv_table); + return false; + } + sv_tot = j; } - sv_tot = j; sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * sv_tot, "sv_array"); loop_nr = 0; + STACK_DECLARE(sv_array); + STACK_INIT(sv_array, sv_tot); + while (1) { float vec_a[3], vec_b[3]; BMLoop *l_a, *l_b; @@ -6284,6 +6296,11 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f l_a_prev = NULL; l_b_prev = NULL; +#define SV_FROM_VERT(v) ( \ + (sv_table[BM_elem_index_get(v)] == INDEX_UNSET) ? \ + ((void)(sv_table[BM_elem_index_get(v)] = STACK_SIZE(sv_array)), STACK_PUSH_RET_PTR(sv_array)) : \ + (&sv_array[sv_table[BM_elem_index_get(v)]])) + /*iterate over the loop*/ v_first = v; do { @@ -6295,8 +6312,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f /* XXX, 'sv' will initialize multiple times, this is suspicious. see [#34024] */ BLI_assert(v != NULL); - BLI_assert(sv_table[BM_elem_index_get(v)] != -1); - sv = &sv_array[sv_table[BM_elem_index_get(v)]]; + BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID); + sv = SV_FROM_VERT(v); sv->v = v; copy_v3_v3(sv->v_co_orig, v->co); sv->loop_nr = loop_nr; @@ -6321,8 +6338,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f if (!e) { BLI_assert(v != NULL); - BLI_assert(sv_table[BM_elem_index_get(v)] != -1); - sv = &sv_array[sv_table[BM_elem_index_get(v)]]; + + BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID); + sv = SV_FROM_VERT(v); + sv->v = v; copy_v3_v3(sv->v_co_orig, v->co); sv->loop_nr = loop_nr; @@ -6411,6 +6430,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f BM_elem_flag_disable(v_prev, BM_ELEM_TAG); } while ((e != v_first->e) && (l_a || l_b)); +#undef SV_FROM_VERT +#undef INDEX_UNSET +#undef INDEX_INVALID + loop_nr++; #undef EDGESLIDE_VERT_IS_INNER @@ -6418,6 +6441,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */ + BLI_assert(STACK_SIZE(sv_array) == sv_tot); + sld->sv = sv_array; sld->totsv = sv_tot; @@ -6425,10 +6450,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f if (t->spacetype == SPACE_VIEW3D) { v3d = t->sa ? t->sa->spacedata.first : NULL; rv3d = t->ar ? t->ar->regiondata : NULL; - use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); + use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); } - calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, true); + calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, true); /* create copies of faces for customdata projection */ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); @@ -6466,7 +6491,7 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); float mval[2] = {(float)t->mval[0], (float)t->mval[1]}; int loop_nr; - bool use_btree_disp = false; + bool use_occlude_geometry = false; View3D *v3d = NULL; RegionView3D *rv3d = NULL; @@ -6513,6 +6538,7 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f bm->elem_index_dirty &= ~BM_VERT; if (!j) { + MEM_freeN(sld); return false; } @@ -6627,10 +6653,10 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f if (t->spacetype == SPACE_VIEW3D) { v3d = t->sa ? t->sa->spacedata.first : NULL; rv3d = t->ar ? t->ar->regiondata : NULL; - use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); + use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); } - calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, false); + calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, false); /* create copies of faces for customdata projection */ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 50168e78dda..a59f9dc43dd 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -87,7 +87,6 @@ typedef struct TransSnap { float snapPoint[3]; /* snapping from this point */ float snapTarget[3]; /* to this point */ float snapNormal[3]; - float snapTangent[3]; char snapNodeBorder; ListBase points; TransSnapPoint *selectedPoint; diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 13cc0c22778..d7b670b6476 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -187,9 +187,7 @@ static void viewAxisCorrectCenter(TransInfo *t, float t_con_center[3]) if (l < min_dist) { float diff[3]; - normalize_v3_v3(diff, t->viewinv[2]); - mul_v3_fl(diff, min_dist - l); - + normalize_v3_v3_length(diff, t->viewinv[2], min_dist - l); sub_v3_v3(t_con_center, diff); } } @@ -225,9 +223,8 @@ static void axisProjection(TransInfo *t, const float axis[3], const float in[3], if (factor < 0.0f) factor *= -factor; else factor *= factor; - copy_v3_v3(out, axis); - normalize_v3(out); - mul_v3_fl(out, -factor); /* -factor makes move down going backwards */ + /* -factor makes move down going backwards */ + normalize_v3_v3_length(out, axis, -factor); } else { float v[3], i1[3], i2[3]; diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 707c60f8701..ad2b40bfef8 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -701,7 +701,6 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob) bPoseChannel *pchan; Bone *bone; int mode = *out_mode; - int hastranslation = 0; int total = 0; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { @@ -729,7 +728,7 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob) } } /* now count, and check if we have autoIK or have to switch from translate to rotate */ - hastranslation = 0; + bool has_translation = false, has_rotation = false; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { bone = pchan->bone; @@ -740,20 +739,29 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob) if (has_targetless_ik(pchan) == NULL) { if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) { if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM) - hastranslation = 1; + has_translation = true; } - else if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) - hastranslation = 1; + else { + if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) + has_translation = true; + } + if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT) + has_rotation = true; } else - hastranslation = 1; + has_translation = true; } } } /* if there are no translatable bones, do rotation */ - if (mode == TFM_TRANSLATION && !hastranslation) { - *out_mode = TFM_ROTATION; + if (mode == TFM_TRANSLATION && !has_translation) { + if (has_rotation) { + *out_mode = TFM_ROTATION; + } + else { + *out_mode = TFM_RESIZE; + } } return total; @@ -5273,13 +5281,9 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob) skip_invert = true; if (skip_invert == false && constinv == false) { - if (constinv == false) - ob->transflag |= OB_NO_CONSTRAINTS; /* BKE_object_where_is_calc_time checks this */ - + ob->transflag |= OB_NO_CONSTRAINTS; /* BKE_object_where_is_calc_time checks this */ BKE_object_where_is_calc(t->scene, ob); - - if (constinv == false) - ob->transflag &= ~OB_NO_CONSTRAINTS; + ob->transflag &= ~OB_NO_CONSTRAINTS; } else BKE_object_where_is_calc(t->scene, ob); @@ -6429,7 +6433,7 @@ static void createTransObject(bContext *C, TransInfo *t) } /* select linked objects, but skip them later */ - if (ob->id.lib != NULL) { + if (ID_IS_LINKED_DATABLOCK(ob)) { td->flag |= TD_SKIP; } @@ -7683,7 +7687,11 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; } - + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + if (is_prop_edit) { /* Proportional Editing... */ if (is_prop_edit_connected) { @@ -7731,14 +7739,27 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { bGPDframe *gpf = gpl->actframe; bGPDstroke *gps; + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + /* undo matrix */ + invert_m4_m4(inverse_diff_mat, diff_mat); + } - /* Make a new frame to work on if the layer's frame and the current scene frame don't match up + /* Make a new frame to work on if the layer's frame and the current scene frame don't match up * - This is useful when animating as it saves that "uh-oh" moment when you realize you've * spent too much time editing the wrong frame... */ // XXX: should this be allowed when framelock is enabled? if (gpf->framenum != cfra) { gpf = gpencil_frame_addcopy(gpl, cfra); + /* in some weird situations (framelock enabled) return NULL */ + if (gpf == NULL) { + continue; + } } /* Loop over strokes, adding TransData for points as needed... */ @@ -7751,7 +7772,10 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; } - + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } /* What we need to include depends on proportional editing settings... */ if (is_prop_edit) { if (is_prop_edit_connected) { @@ -7820,9 +7844,18 @@ static void createTransGPencil(bContext *C, TransInfo *t) /* screenspace */ td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; - copy_m3_m4(td->smtx, t->persmat); - copy_m3_m4(td->mtx, t->persinv); - unit_m3(td->axismtx); + /* apply parent transformations */ + if (gpl->parent == NULL) { + copy_m3_m4(td->smtx, t->persmat); + copy_m3_m4(td->mtx, t->persinv); + unit_m3(td->axismtx); + } + else { + /* apply matrix transformation relative to parent */ + copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */ + copy_m3_m4(td->mtx, diff_mat); /* display position */ + copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */ + } } else { /* configure 2D dataspace points so that they don't play up... */ @@ -7831,9 +7864,18 @@ static void createTransGPencil(bContext *C, TransInfo *t) // XXX: matrices may need to be different? } - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); - unit_m3(td->axismtx); // XXX? + /* apply parent transformations */ + if (gpl->parent == NULL) { + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + unit_m3(td->axismtx); // XXX? + } + else { + /* apply matrix transformation relative to parent */ + copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */ + copy_m3_m4(td->mtx, diff_mat); /* display position */ + copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */ + } } /* Triangulation must be calculated again, so save the stroke for recalc function */ td->extra = gps; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 67740644afe..f78a23be7b8 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -563,7 +563,7 @@ static void recalcData_nla(TransInfo *t) } /* Use RNA to write the values to ensure that constraints on these are obeyed - * (e.g. for transition strips, the values are taken from the neighbours) + * (e.g. for transition strips, the values are taken from the neighbors) * * NOTE: we write these twice to avoid truncation errors which can arise when * moving the strips a large distance using numeric input [#33852] @@ -975,7 +975,9 @@ static void recalcData_gpencil_strokes(TransInfo *t) TransData *td = t->data; for (int i = 0; i < t->total; i++, td++) { bGPDstroke *gps = td->extra; - gps->flag |= GP_STROKE_RECALC_CACHES; + if (gps != NULL) { + gps->flag |= GP_STROKE_RECALC_CACHES; + } } } diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c index 992fde0cce3..075f311db72 100644 --- a/source/blender/editors/transform/transform_manipulator.c +++ b/source/blender/editors/transform/transform_manipulator.c @@ -58,6 +58,7 @@ #include "BKE_pointcache.h" #include "BKE_editmesh.h" #include "BKE_lattice.h" +#include "BKE_gpencil.h" #include "BIF_gl.h" @@ -68,6 +69,7 @@ #include "ED_curve.h" #include "ED_particle.h" #include "ED_view3d.h" +#include "ED_gpencil.h" #include "UI_resources.h" @@ -288,24 +290,49 @@ static int calc_manipulator_stats(const bContext *C) zero_v3(scene->twcent); if (is_gp_edit) { - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - /* we're only interested in selected points here... */ - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; - - /* Change selection status of all points, then make the stroke match */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - calc_tw_center(scene, &pt->x); - totsel++; + float diff_mat[4][4]; + float fpt[3]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + + for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + + /* we're only interested in selected points here... */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + + /* Change selection status of all points, then make the stroke match */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + if (gpl->parent == NULL) { + calc_tw_center(scene, &pt->x); + totsel++; + } + else { + mul_v3_m4v3(fpt, diff_mat, &pt->x); + calc_tw_center(scene, fpt); + totsel++; + } + } + } } } } } - CTX_DATA_END; - + + /* selection center */ if (totsel) { mul_v3_fl(scene->twcent, 1.0f / (float)totsel); /* centroid! */ @@ -995,8 +1022,7 @@ static void draw_manipulator_rotate( vec[0] = 0; // XXX (float)(t->mouse.imval[0] - t->center2d[0]); vec[1] = 0; // XXX (float)(t->mouse.imval[1] - t->center2d[1]); vec[2] = 0.0f; - normalize_v3(vec); - mul_v3_fl(vec, 1.2f * size); + normalize_v3_length(vec, 1.2f * size); glBegin(GL_LINES); glVertex3f(0.0f, 0.0f, 0.0f); glVertex3fv(vec); @@ -1516,8 +1542,7 @@ static void draw_manipulator_rotate_cyl( vec[0] = 0; // XXX (float)(t->mouse.imval[0] - t->center2d[0]); vec[1] = 0; // XXX (float)(t->mouse.imval[1] - t->center2d[1]); vec[2] = 0.0f; - normalize_v3(vec); - mul_v3_fl(vec, 1.2f * size); + normalize_v3_length(vec, 1.2f * size); glBegin(GL_LINES); glVertex3f(0.0, 0.0, 0.0); glVertex3fv(vec); diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 0bb64315845..f8bb124e943 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -984,15 +984,6 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) } if (found == true) { - float tangent[3]; - - sub_v2_v2v2(tangent, loc, t->tsnap.snapPoint); - tangent[2] = 0.0f; - - if (!is_zero_v3(tangent)) { - copy_v3_v3(t->tsnap.snapTangent, tangent); - } - copy_v3_v3(t->tsnap.snapPoint, loc); copy_v3_v3(t->tsnap.snapNormal, no); diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index e85b686b5b3..1d4872cca7a 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -110,6 +110,12 @@ struct SnapObjectContext { }; +enum eViewProj { + VIEW_PROJ_NONE = -1, + VIEW_PROJ_ORTHO = 0, + VIEW_PROJ_PERSP = -1, +}; + static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt); @@ -137,10 +143,6 @@ struct RayCastAll_Data { Object *ob; unsigned int ob_uuid; - /* DerivedMesh only */ - DerivedMesh *dm; - const struct MLoopTri *dm_looptri; - /* output data */ ListBase *hit_list; bool retval; @@ -218,197 +220,557 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH /* -------------------------------------------------------------------- */ -/** \name Internal Object Snapping API +/** \Common utilities * \{ */ -static bool snapEdge( - const ARegion *ar, const float v1co[3], const short v1no[3], const float v2co[3], const short v2no[3], - float obmat[4][4], float timat[3][3], const float mval_fl[2], - const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3], - /* read/write args */ - float *ray_depth, float *dist_px, - /* return args */ - float r_loc[3], float r_no[3]) -{ - float intersect[3] = {0, 0, 0}, ray_end[3], dvec[3]; - int result; - bool retval = false; - copy_v3_v3(ray_end, ray_normal_local); - mul_v3_fl(ray_end, 2000); - add_v3_v3v3(ray_end, ray_start_local, ray_end); +/** + * Struct that kepts basic information about a BVHTree build from a editmesh. + */ +typedef struct BVHTreeFromMeshType { + void *userdata; + char type; +} BVHTreeFromMeshType; + +typedef struct PreDefProject { + float pmat[4][4]; /* perspective matrix multiplied by object matrix */ + float win_half[2]; + float dist_px_sq; +} PreDefProject; + +static void precalc_project( + PreDefProject *projectdefs, const ARegion *ar, + const float dist_px, float obmat[4][4]) +{ + float (*pmat)[4] = ((RegionView3D *)ar->regiondata)->persmat; + if (obmat) { + mul_m4_m4m4(projectdefs->pmat, pmat, obmat); + } + else { + copy_m4_m4(projectdefs->pmat, pmat); + } + projectdefs->win_half[0] = ar->winx / 2; + projectdefs->win_half[1] = ar->winy / 2; + projectdefs->dist_px_sq = SQUARE(dist_px); +} - /* dvec used but we don't care about result */ - result = isect_line_line_v3(v1co, v2co, ray_start_local, ray_end, intersect, dvec); +/** + * From a threshold (maximum distance to snap in pixels) returns: + * + * - The *real* distance (3D) if you are in orthographic-view. + * - The *tangent* (view cone radius at distance 1.0) if you are in perspective-view. + */ +static float dist_px_to_dist3d_or_tangent(const ARegion *ar, const float dist_px) +{ + const RegionView3D *rv3d = ar->regiondata; + if (ar->winx >= ar->winy) + return 2 * (dist_px / ar->winx) / rv3d->winmat[0][0]; + else + return 2 * (dist_px / ar->winy) / rv3d->winmat[1][1]; +} - if (result) { - float edge_loc[3], vec[3]; - float mul; +static const float *get_vert_co(const BVHTreeFromMeshType *meshdata, const int index) +{ + switch (meshdata->type) { + case SNAP_MESH: + { + BVHTreeFromMesh *data = meshdata->userdata; + const MVert *vert = data->vert; + return vert[index].co; + } + case SNAP_EDIT_MESH: + { + BVHTreeFromEditMesh *data = meshdata->userdata; + BMVert *eve = BM_vert_at_index(data->em->bm, index); + return eve->co; + } + } + return NULL; +} - /* check for behind ray_start */ - sub_v3_v3v3(dvec, intersect, ray_start_local); +static void copy_vert_no(const BVHTreeFromMeshType *meshdata, const int index, float r_no[3]) +{ + switch (meshdata->type) { + case SNAP_MESH: + { + BVHTreeFromMesh *data = meshdata->userdata; + const MVert *vert = data->vert; + normal_short_to_float_v3(r_no, vert->no); + break; + } + case SNAP_EDIT_MESH: + { + BVHTreeFromEditMesh *data = meshdata->userdata; + BMVert *eve = BM_vert_at_index(data->em->bm, index); + copy_v3_v3(r_no, eve->no); + break; + } + } +} - sub_v3_v3v3(edge_loc, v1co, v2co); - sub_v3_v3v3(vec, intersect, v2co); +static void get_edge_verts( + const BVHTreeFromMeshType *meshdata, const int index, + const float *v_pair[2]) +{ + switch (meshdata->type) { + case SNAP_MESH: + { + BVHTreeFromMesh *data = meshdata->userdata; - mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc); + const MVert *vert = data->vert; + const MEdge *edge = data->edge + index; - if (mul > 1) { - mul = 1; - copy_v3_v3(intersect, v1co); + v_pair[0] = vert[edge->v1].co; + v_pair[1] = vert[edge->v2].co; + break; } - else if (mul < 0) { - mul = 0; - copy_v3_v3(intersect, v2co); + case SNAP_EDIT_MESH: + { + BVHTreeFromEditMesh *data = meshdata->userdata; + BMEdge *eed = BM_edge_at_index(data->em->bm, index); + + v_pair[0] = eed->v1->co; + v_pair[1] = eed->v2->co; + break; } + } +} - if (dot_v3v3(ray_normal_local, dvec) > 0) { - float location[3]; - float new_depth; - float screen_loc[2]; - float new_dist; +#define V3_MUL_ELEM(a, b) \ + (a)[0] * (b)[0], \ + (a)[1] * (b)[1], \ + (a)[2] * (b)[2] - copy_v3_v3(location, intersect); +static bool test_vert_dist( + const float vco[3], const float ray_co[3], const float ray_dir[3], + const float ray_depth_range[2], const float scale[3], + /* read/write args */ + float *ray_depth, float *dist_to_ray_sq, + /* return args */ + float r_co[3]) +{ + const float vco_sc[3] = {V3_MUL_ELEM(vco, scale)}; + const float origin_sc[3] = {V3_MUL_ELEM(ray_co, scale)}; + const float dir_sc[3] = {V3_MUL_ELEM(ray_dir, scale)}; - mul_m4_v3(obmat, location); + float depth, dist_sq; + dist_sq = dist_squared_to_ray_v3(origin_sc, dir_sc, vco_sc, &depth); - new_depth = len_v3v3(location, ray_start); + if (depth < ray_depth_range[0]) { + return false; + } - if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - new_dist = len_manhattan_v2v2(mval_fl, screen_loc); - } - else { - new_dist = TRANSFORM_DIST_MAX_PX; - } + if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) { + *dist_to_ray_sq = dist_sq; - /* 10% threshold if edge is closer but a bit further - * this takes care of series of connected edges a bit slanted w.r.t the viewport - * otherwise, it would stick to the verts of the closest edge and not slide along merrily - * */ - if (new_dist <= *dist_px && new_depth < *ray_depth * 1.001f) { - float n1[3], n2[3]; + copy_v3_v3(r_co, vco); - *ray_depth = new_depth; - retval = true; + *ray_depth = depth; + return true; + } + return false; +} - sub_v3_v3v3(edge_loc, v1co, v2co); - sub_v3_v3v3(vec, intersect, v2co); +static bool test_edge_dist( + const float v1[3], const float v2[3], const float ray_co[3], const float ray_dir[3], + const float ray_depth_range[2], const float scale[3], + /* read/write args */ + float *ray_depth, float *dist_to_ray_sq, + /* return args */ + float r_co[3]) +{ + const float v1_sc[3] = {V3_MUL_ELEM(v1, scale)}; + const float v2_sc[3] = {V3_MUL_ELEM(v2, scale)}; + const float co_sc[3] = {V3_MUL_ELEM(ray_co, scale)}; + const float dir_sc[3] = {V3_MUL_ELEM(ray_dir, scale)}; - mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc); + float tmp_co[3], depth, dist_sq; + dist_sq = dist_squared_ray_to_seg_v3(co_sc, dir_sc, v1_sc, v2_sc, tmp_co, &depth); - if (r_no) { - normal_short_to_float_v3(n1, v1no); - normal_short_to_float_v3(n2, v2no); - interp_v3_v3v3(r_no, n2, n1, mul); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } + if (depth < ray_depth_range[0]) { + return false; + } - copy_v3_v3(r_loc, location); + if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) { + *dist_to_ray_sq = dist_sq; - *dist_px = new_dist; - } + tmp_co[0] /= scale[0]; + tmp_co[1] /= scale[1]; + tmp_co[2] /= scale[2]; + + copy_v3_v3(r_co, tmp_co); + + *ray_depth = depth; + return true; + } + return false; +} + +#undef V3_MUL_ELEM + +static bool test_projected_vert_dist( + PreDefProject *projectdefs, + const float co[3], const enum eViewProj view_proj, + const float mval[2], const float depth_range[2], + float r_co[3]) +{ + float depth; + float(*pmat)[4] = projectdefs->pmat; + if (view_proj == VIEW_PROJ_PERSP) { + depth = mul_project_m4_v3_zfac(pmat, co); + if (depth < depth_range[0] || depth > depth_range[1]) { + return false; } } - return retval; + float co2d[2] = { + (dot_m4_v3_row_x(pmat, co) + pmat[3][0]), + (dot_m4_v3_row_y(pmat, co) + pmat[3][1]), + }; + + if (view_proj == VIEW_PROJ_PERSP) { + mul_v2_fl(co2d, 1 / depth); + } + + co2d[0] += 1.0f; + co2d[1] += 1.0f; + co2d[0] *= projectdefs->win_half[0]; + co2d[1] *= projectdefs->win_half[1]; + + const float dist_sq = len_squared_v2v2(mval, co2d); + if (dist_sq < projectdefs->dist_px_sq) { + copy_v3_v3(r_co, co); + projectdefs->dist_px_sq = dist_sq; + return true; + } + return false; } -static bool snapVertex( - const ARegion *ar, const float vco[3], const float vno[3], - float obmat[4][4], float timat[3][3], const float mval_fl[2], - const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3], - /* read/write args */ - float *ray_depth, float *dist_px, - /* return args */ - float r_loc[3], float r_no[3]) +static bool test_projected_edge_dist( + PreDefProject *projectdefs, + const float va[3], const float vb[3], const float ray_start[3], const float ray_normal[3], + const enum eViewProj view_proj, const float mval[2], const float depth_range[2], + float r_co[3]) { - bool retval = false; - float dvec[3]; - sub_v3_v3v3(dvec, vco, ray_start_local); + float tmp_co[3], depth; + dist_squared_ray_to_seg_v3(ray_start, ray_normal, va, vb, tmp_co, &depth); + return test_projected_vert_dist(projectdefs, tmp_co, view_proj, mval, depth_range, r_co); +} - if (dot_v3v3(ray_normal_local, dvec) > 0) { - float location[3]; - float new_depth; - float screen_loc[2]; - float new_dist; +/** \} */ - copy_v3_v3(location, vco); - mul_m4_v3(obmat, location); +/* -------------------------------------------------------------------- */ - new_depth = len_v3v3(location, ray_start); +/** \Walk DFS + * \{ */ +typedef struct Object_Nearest2dPrecalc { + float ray_origin_local[3]; + float ray_direction_local[3]; + float ray_inv_dir[3]; + + PreDefProject projectdefs; + float mval[2]; + bool sign[3]; + bool r_axis_closest[3]; + float depth_range[2]; + + void *userdata; + int index; + float co[3]; + float no[3]; +} Object_Nearest2dPrecalc; + + +static void nearest2d_precalc( + Object_Nearest2dPrecalc *neasrest_precalc, const ARegion *ar, + const float dist_px, float obmat[4][4], + const float ray_origin_local[3], const float ray_direction_local[3], + const float mval[2], const float depth_range[2]) +{ + precalc_project(&neasrest_precalc->projectdefs, ar, dist_px, obmat); + copy_v3_v3(neasrest_precalc->ray_origin_local, ray_origin_local); + copy_v3_v3(neasrest_precalc->ray_direction_local, ray_direction_local); + copy_v2_v2(neasrest_precalc->mval, mval); + copy_v2_v2(neasrest_precalc->depth_range, depth_range); - if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - new_dist = len_manhattan_v2v2(mval_fl, screen_loc); - } - else { - new_dist = TRANSFORM_DIST_MAX_PX; - } + for (int i = 0; i < 3; i++) { + neasrest_precalc->ray_inv_dir[i] = + (neasrest_precalc->ray_direction_local[i] != 0.0f) ? + (1.0f / neasrest_precalc->ray_direction_local[i]) : FLT_MAX; + neasrest_precalc->sign[i] = (neasrest_precalc->ray_inv_dir[i] < 0.0f); + neasrest_precalc->r_axis_closest[i] = true; + } +} +static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *user_data) +{ + Object_Nearest2dPrecalc *data = user_data; + float local_bvmin[3], local_bvmax[3]; + if (data->sign[0]) { + local_bvmin[0] = bounds[0].max; + local_bvmax[0] = bounds[0].min; + } + else { + local_bvmin[0] = bounds[0].min; + local_bvmax[0] = bounds[0].max; + } + if (data->sign[1]) { + local_bvmin[1] = bounds[1].max; + local_bvmax[1] = bounds[1].min; + } + else { + local_bvmin[1] = bounds[1].min; + local_bvmax[1] = bounds[1].max; + } + if (data->sign[2]) { + local_bvmin[2] = bounds[2].max; + local_bvmax[2] = bounds[2].min; + } + else { + local_bvmin[2] = bounds[2].min; + local_bvmax[2] = bounds[2].max; + } - if (new_dist <= *dist_px && new_depth < *ray_depth) { - *ray_depth = new_depth; - retval = true; + const float tmin[3] = { + (local_bvmin[0] - data->ray_origin_local[0]) * data->ray_inv_dir[0], + (local_bvmin[1] - data->ray_origin_local[1]) * data->ray_inv_dir[1], + (local_bvmin[2] - data->ray_origin_local[2]) * data->ray_inv_dir[2], + }; + const float tmax[3] = { + (local_bvmax[0] - data->ray_origin_local[0]) * data->ray_inv_dir[0], + (local_bvmax[1] - data->ray_origin_local[1]) * data->ray_inv_dir[1], + (local_bvmax[2] - data->ray_origin_local[2]) * data->ray_inv_dir[2], + }; + float va[3], vb[3]; + float rtmin, rtmax; + int main_axis; + + if ((tmax[0] <= tmax[1]) && (tmax[0] <= tmax[2])) { + rtmax = tmax[0]; + va[0] = vb[0] = local_bvmax[0]; + main_axis = 3; + data->r_axis_closest[0] = data->sign[0]; + } + else if ((tmax[1] <= tmax[0]) && (tmax[1] <= tmax[2])) { + rtmax = tmax[1]; + va[1] = vb[1] = local_bvmax[1]; + main_axis = 2; + data->r_axis_closest[1] = data->sign[1]; + } + else { + rtmax = tmax[2]; + va[2] = vb[2] = local_bvmax[2]; + main_axis = 1; + data->r_axis_closest[2] = data->sign[2]; + } - copy_v3_v3(r_loc, location); + if ((tmin[0] >= tmin[1]) && (tmin[0] >= tmin[2])) { + rtmin = tmin[0]; + va[0] = vb[0] = local_bvmin[0]; + main_axis -= 3; + data->r_axis_closest[0] = !data->sign[0]; + } + else if ((tmin[1] >= tmin[0]) && (tmin[1] >= tmin[2])) { + rtmin = tmin[1]; + va[1] = vb[1] = local_bvmin[1]; + main_axis -= 1; + data->r_axis_closest[1] = !data->sign[1]; + } + else { + rtmin = tmin[2]; + va[2] = vb[2] = local_bvmin[2]; + main_axis -= 2; + data->r_axis_closest[2] = !data->sign[2]; + } + if (main_axis < 0) { + main_axis += 3; + } - if (r_no) { - copy_v3_v3(r_no, vno); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } + /* if rtmin < rtmax, ray intersect `AABB` */ + if (rtmin <= rtmax) { +#ifdef IGNORE_BEHIND_RAY + /* `if rtmax < depth_min`, the whole `AABB` is behind us */ + if (rtmax < min_depth) { + return fallback; + } +#endif + const float proj = rtmin * data->ray_direction_local[main_axis]; + data->r_axis_closest[main_axis] = (proj - va[main_axis]) < (vb[main_axis] - proj); + return true; + } +#ifdef IGNORE_BEHIND_RAY + /* `if rtmin < depth_min`, the whole `AABB` is behing us */ + else if (rtmin < min_depth) { + return fallback; + } +#endif + if (data->sign[main_axis]) { + va[main_axis] = local_bvmax[main_axis]; + vb[main_axis] = local_bvmin[main_axis]; + } + else { + va[main_axis] = local_bvmin[main_axis]; + vb[main_axis] = local_bvmax[main_axis]; + } + float scale = fabsf(local_bvmax[main_axis] - local_bvmin[main_axis]); + + float (*pmat)[4] = data->projectdefs.pmat; + float depth_a = mul_project_m4_v3_zfac(pmat, va); + float depth_b = depth_a + pmat[main_axis][3] * scale; + + float va2d[2] = { + (dot_m4_v3_row_x(pmat, va) + pmat[3][0]), + (dot_m4_v3_row_y(pmat, va) + pmat[3][1]), + }; + float vb2d[2] = { + (va2d[0] + pmat[main_axis][0] * scale) / depth_b, + (va2d[1] + pmat[main_axis][1] * scale) / depth_b, + }; - *dist_px = new_dist; + va2d[0] /= depth_a; + va2d[1] /= depth_a; + + va2d[0] += 1.0f; + va2d[1] += 1.0f; + vb2d[0] += 1.0f; + vb2d[1] += 1.0f; + + va2d[0] *= data->projectdefs.win_half[0]; + va2d[1] *= data->projectdefs.win_half[1]; + vb2d[0] *= data->projectdefs.win_half[0]; + vb2d[1] *= data->projectdefs.win_half[1]; + + //float dvec[2], edge[2], rdist; + //sub_v2_v2v2(dvec, data->mval, va2d); + //sub_v2_v2v2(edge, vb2d, va2d); + float rdist; + short dvec[2] = {data->mval[0] - va2d[0], data->mval[1] - va2d[1]}; + short edge[2] = {vb2d[0] - va2d[0], vb2d[1] - va2d[1]}; + float lambda = dvec[0] * edge[0] + dvec[1] * edge[1]; + if (lambda != 0.0f) { + lambda /= edge[0] * edge[0] + edge[1] * edge[1]; + if (lambda <= 0.0f) { + rdist = len_squared_v2v2(data->mval, va2d); + data->r_axis_closest[main_axis] = true; } + else if (lambda >= 1.0f) { + rdist = len_squared_v2v2(data->mval, vb2d); + data->r_axis_closest[main_axis] = false; + } + else { + va2d[0] += edge[0] * lambda; + va2d[1] += edge[1] * lambda; + rdist = len_squared_v2v2(data->mval, va2d); + data->r_axis_closest[main_axis] = lambda < 0.5f; + } + } + else { + rdist = len_squared_v2v2(data->mval, va2d); } + return rdist < data->projectdefs.dist_px_sq; +} - return retval; +static bool cb_walk_leaf_snap_vert(const BVHTreeAxisRange *bounds, int index, void *userdata) +{ + struct Object_Nearest2dPrecalc *neasrest_precalc = userdata; + const float co[3] = { + (bounds[0].min + bounds[0].max) / 2, + (bounds[1].min + bounds[1].max) / 2, + (bounds[2].min + bounds[2].max) / 2, + }; + + /* Currently the `BLI_bvhtree_walk_dfs` is being used only in the perspective view mode (VIEW_PROJ_PERSP) + * It could be used in orthographic view mode too (VIEW_PROJ_ORTHO), + * but in this case the `BLI_bvhtree_find_nearest_to_ray` is more efficient.*/ + if (test_projected_vert_dist( + &neasrest_precalc->projectdefs, co, VIEW_PROJ_PERSP, + neasrest_precalc->mval, neasrest_precalc->depth_range, + neasrest_precalc->co)) + { + copy_vert_no(neasrest_precalc->userdata, index, neasrest_precalc->no); + neasrest_precalc->index = index; + } + return true; +} + +static bool cb_walk_leaf_snap_edge(const BVHTreeAxisRange *UNUSED(bounds), int index, void *userdata) +{ + struct Object_Nearest2dPrecalc *neasrest_precalc = userdata; + + const float *v_pair[2]; + get_edge_verts(neasrest_precalc->userdata, index, v_pair); + + /* Currently the `BLI_bvhtree_walk_dfs` is being used only in the perspective view mode (VIEW_PROJ_PERSP) + * It could be used in orthographic view mode too (VIEW_PROJ_ORTHO), + * but in this case the `BLI_bvhtree_find_nearest_to_ray` is more efficient.*/ + if (test_projected_edge_dist( + &neasrest_precalc->projectdefs, v_pair[0], v_pair[1], + neasrest_precalc->ray_origin_local, neasrest_precalc->ray_direction_local, + VIEW_PROJ_PERSP, neasrest_precalc->mval, neasrest_precalc->depth_range, + neasrest_precalc->co)) + { + sub_v3_v3v3(neasrest_precalc->no, v_pair[0], v_pair[1]); + neasrest_precalc->index = index; + } + return true; } +static bool cb_nearest_walk_order(const BVHTreeAxisRange *UNUSED(bounds), char axis, void *userdata) +{ + const bool *r_axis_closest = ((struct Object_Nearest2dPrecalc *)userdata)->r_axis_closest; + return r_axis_closest[axis]; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ + +/** \name Internal Object Snapping API + * \{ */ + static bool snapArmature( const ARegion *ar, Object *ob, bArmature *arm, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], + const short snap_to, const float origin[3], const float dir[3], + const float mval[2], const enum eViewProj view_proj, const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { - float imat[4][4]; - float ray_start_local[3], ray_normal_local[3]; bool retval = false; - invert_m4_m4(imat, obmat); + float ray_start_local[3], ray_normal_local[3]; + if (snap_to != SCE_SNAP_MODE_VERTEX) { + float imat[4][4]; + invert_m4_m4(imat, obmat); - mul_v3_m4v3(ray_start_local, imat, ray_start); - mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); + copy_v3_v3(ray_start_local, origin); + copy_v3_v3(ray_normal_local, dir); + mul_m4_v3(imat, ray_start_local); + mul_mat3_m4_v3(imat, ray_normal_local); + } - if (arm->edbo) { - EditBone *eBone; + PreDefProject projectdefs; + precalc_project(&projectdefs, ar, *dist_px, obmat); - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + if (arm->edbo) { + for (EditBone *eBone = arm->edbo->first; eBone; eBone = eBone->next) { if (eBone->layer & arm->layer) { /* skip hidden or moving (selected) bones */ if ((eBone->flag & (BONE_HIDDEN_A | BONE_ROOTSEL | BONE_TIPSEL)) == 0) { switch (snap_to) { case SCE_SNAP_MODE_VERTEX: - retval |= snapVertex( - ar, eBone->head, NULL, obmat, NULL, mval, dist_px, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, NULL); - retval |= snapVertex( - ar, eBone->tail, NULL, obmat, NULL, mval, dist_px, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, eBone->head, view_proj, mval, depth_range, r_loc); + retval |= test_projected_vert_dist( + &projectdefs, eBone->tail, view_proj, mval, depth_range, r_loc); break; case SCE_SNAP_MODE_EDGE: - retval |= snapEdge( - ar, eBone->head, NULL, eBone->tail, NULL, - obmat, NULL, mval, dist_px, - ray_start, ray_start_local, ray_normal_local, - ray_depth, r_loc, NULL); + retval |= test_projected_edge_dist( + &projectdefs, eBone->head, eBone->tail, ray_start_local, ray_normal_local, + view_proj, mval, depth_range, r_loc); break; } } @@ -416,11 +778,8 @@ static bool snapArmature( } } else if (ob->pose && ob->pose->chanbase.first) { - bPoseChannel *pchan; - Bone *bone; - - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - bone = pchan->bone; + for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + Bone *bone = pchan->bone; /* skip hidden bones */ if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { const float *head_vec = pchan->pose_head; @@ -428,61 +787,49 @@ static bool snapArmature( switch (snap_to) { case SCE_SNAP_MODE_VERTEX: - retval |= snapVertex( - ar, head_vec, NULL, obmat, NULL, mval, dist_px, - ray_start, ray_start_local, ray_normal_local, - ray_depth, r_loc, NULL); - retval |= snapVertex( - ar, tail_vec, NULL, obmat, NULL, mval, dist_px, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, head_vec, view_proj, mval, depth_range, r_loc); + retval |= test_projected_vert_dist( + &projectdefs, tail_vec, view_proj, mval, depth_range, r_loc); break; case SCE_SNAP_MODE_EDGE: - retval |= snapEdge( - ar, head_vec, NULL, tail_vec, NULL, - obmat, NULL, mval, dist_px, - ray_start, ray_start_local, ray_normal_local, - ray_depth, r_loc, NULL); + retval |= test_projected_edge_dist( + &projectdefs, head_vec, tail_vec, ray_start_local, ray_normal_local, + view_proj, mval, depth_range, r_loc); break; } } } } - - return retval; + if (retval) { + *dist_px = sqrtf(projectdefs.dist_px_sq); + mul_m4_v3(obmat, r_loc); + return true; + } + return false; } static bool snapCurve( const ARegion *ar, Object *ob, Curve *cu, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], + const short snap_to, const float mval[2], const enum eViewProj view_proj, + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { - float imat[4][4]; - float ray_start_local[3], ray_normal_local[3]; bool retval = false; - int u; - - Nurb *nu; /* only vertex snapping mode (eg control points and handles) supported for now) */ if (snap_to != SCE_SNAP_MODE_VERTEX) { return retval; } - invert_m4_m4(imat, obmat); - - copy_v3_v3(ray_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); - - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); + PreDefProject projectdefs; + precalc_project(&projectdefs, ar, *dist_px, obmat); - for (nu = (ob->mode == OB_MODE_EDIT ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { - for (u = 0; u < nu->pntsu; u++) { + for (Nurb *nu = (ob->mode == OB_MODE_EDIT ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { + for (int u = 0; u < nu->pntsu; u++) { switch (snap_to) { case SCE_SNAP_MODE_VERTEX: { @@ -492,29 +839,20 @@ static bool snapCurve( if (nu->bezt[u].f2 & SELECT || nu->bezt[u].hide != 0) { break; } - retval |= snapVertex( - ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval, - ray_start, ray_start_local, ray_normal_local, - ray_depth, dist_px, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bezt[u].vec[1], view_proj, mval, depth_range, r_loc); /* don't snap if handle is selected (moving), or if it is aligning to a moving handle */ if (!(nu->bezt[u].f1 & SELECT) && !(nu->bezt[u].h1 & HD_ALIGN && nu->bezt[u].f3 & SELECT)) { - retval |= snapVertex( - ar, nu->bezt[u].vec[0], NULL, obmat, NULL, mval, - ray_start, ray_start_local, ray_normal_local, - ray_depth, dist_px, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bezt[u].vec[0], view_proj, mval, depth_range, r_loc); } if (!(nu->bezt[u].f3 & SELECT) && !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) { - retval |= snapVertex( - ar, nu->bezt[u].vec[2], NULL, obmat, NULL, mval, - ray_start, ray_start_local, ray_normal_local, - ray_depth, dist_px, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bezt[u].vec[2], view_proj, mval, depth_range, r_loc); } } else { @@ -522,29 +860,20 @@ static bool snapCurve( if (nu->bp[u].f1 & SELECT || nu->bp[u].hide != 0) { break; } - retval |= snapVertex( - ar, nu->bp[u].vec, NULL, obmat, NULL, mval, - ray_start, ray_start_local, ray_normal_local, - ray_depth, dist_px, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bp[u].vec, view_proj, mval, depth_range, r_loc); } } else { /* curve is not visible outside editmode if nurb length less than two */ if (nu->pntsu > 1) { if (nu->bezt) { - retval |= snapVertex( - ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval, - ray_start, ray_start_local, ray_normal_local, - ray_depth, dist_px, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bezt[u].vec[1], view_proj, mval, depth_range, r_loc); } else { - retval |= snapVertex( - ar, nu->bp[u].vec, NULL, obmat, NULL, mval, - ray_start, ray_start_local, ray_normal_local, - ray_depth, dist_px, - r_loc, NULL); + retval |= test_projected_vert_dist( + &projectdefs, nu->bp[u].vec, view_proj, mval, depth_range, r_loc); } } } @@ -555,44 +884,42 @@ static bool snapCurve( } } } - return retval; + if (retval) { + *dist_px = sqrtf(projectdefs.dist_px_sq); + mul_m4_v3(obmat, r_loc); + return true; + } + return false; } /* may extend later (for now just snaps to empty center) */ static bool snapEmpty( const ARegion *ar, Object *ob, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], + const short snap_to, const float mval[2], const enum eViewProj view_proj, + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { - float imat[4][4]; - float ray_start_local[3], ray_normal_local[3]; bool retval = false; if (ob->transflag & OB_DUPLI) { return retval; } - /* for now only vertex supported */ - if (snap_to != SCE_SNAP_MODE_VERTEX) { - return retval; - } - - invert_m4_m4(imat, obmat); - - mul_v3_m4v3(ray_start_local, imat, ray_start); - mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); + /* for now only vertex supported */ switch (snap_to) { case SCE_SNAP_MODE_VERTEX: { - const float zero_co[3] = {0.0f}; - retval |= snapVertex( - ar, zero_co, NULL, obmat, NULL, mval, dist_px, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, NULL); + PreDefProject projectdefs; + precalc_project(&projectdefs, ar, *dist_px, NULL); + float tmp_co[3]; + copy_v3_v3(tmp_co, obmat[3]); + if (test_projected_vert_dist(&projectdefs, tmp_co, view_proj, mval, depth_range, r_loc)) { + *dist_px = sqrtf(projectdefs.dist_px_sq); + retval = true; + } break; } default: @@ -603,19 +930,23 @@ static bool snapEmpty( } static bool snapCamera( - const ARegion *ar, Scene *scene, Object *object, float obmat[4][4], - const float mval[2], const short snap_to, - const float ray_start[3], const float ray_normal[3], + const SnapObjectContext *sctx, Object *object, float obmat[4][4], + const short snap_to, const float mval[2], const enum eViewProj view_proj, + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { + Scene *scene = sctx->scene; + + PreDefProject projectdefs; + precalc_project(&projectdefs, sctx->v3d_data.ar, *dist_px, NULL); + float orig_camera_mat[4][4], orig_camera_imat[4][4], imat[4][4]; bool retval = false; MovieClip *clip = BKE_object_movieclip_get(scene, object, false); MovieTracking *tracking; - float ray_start_local[3], ray_normal_local[3]; if (clip == NULL) { return retval; @@ -646,9 +977,6 @@ static bool snapCamera( reconstructed_camera_imat[4][4]; float (*vertex_obmat)[4]; - copy_v3_v3(ray_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); - if ((tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0) { BKE_tracking_camera_get_reconstructed_interpolate(tracking, tracking_object, CFRA, reconstructed_camera_mat); @@ -665,21 +993,16 @@ static bool snapCamera( copy_v3_v3(bundle_pos, track->bundle_pos); if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { - mul_m4_v3(orig_camera_imat, ray_start_local); - mul_mat3_m4_v3(orig_camera_imat, ray_normal_local); vertex_obmat = orig_camera_mat; } else { mul_m4_v3(reconstructed_camera_imat, bundle_pos); - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); vertex_obmat = obmat; } - retval |= snapVertex( - ar, bundle_pos, NULL, vertex_obmat, NULL, mval, dist_px, - ray_start, ray_start_local, ray_normal_local, ray_depth, - r_loc, NULL); + mul_m4_v3(vertex_obmat, bundle_pos); + retval |= test_projected_vert_dist( + &projectdefs, bundle_pos, view_proj, mval, depth_range, r_loc); } } @@ -689,7 +1012,11 @@ static bool snapCamera( break; } - return retval; + if (retval) { + *dist_px = sqrtf(projectdefs.dist_px_sq); + return true; + } + return false; } static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) @@ -700,149 +1027,45 @@ static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) struct NearestDM_Data { void *bvhdata; - bool is_persp; - const float *ray_depth_range; - + const float *depth_range; float *ray_depth; }; -static bool test_vert( - const float vco[3], const float vno[3], const float ray_co[3], const float ray_dir[3], - const float ray_depth_range[2], const float scale[3], const bool is_persp, - /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, - /* return args */ - float r_co[3], float r_no[3]) -{ - const float vco_sc[3] = { - vco[0] * scale[0], - vco[1] * scale[1], - vco[2] * scale[2], - }; - const float co_sc[3] = { - ray_co[0] * scale[0], - ray_co[1] * scale[1], - ray_co[2] * scale[2], - }; - const float dir_sc[3] = { - ray_dir[0] * scale[0], - ray_dir[1] * scale[1], - ray_dir[2] * scale[2], - }; - - float depth; - float dist_sq = dist_squared_to_ray_v3(co_sc, dir_sc, vco_sc, &depth); - - if (depth < ray_depth_range[0]) - return false; - - if (is_persp) - dist_sq /= SQUARE(depth); - - if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) { - *dist_to_ray_sq = dist_sq; - - copy_v3_v3(r_co, vco); - - if (vno) { - copy_v3_v3(r_no, vno); - } - - *ray_depth = depth; - return true; - } - return false; -} - -static bool test_edge( - const float v1[3], const float v2[3], const float ray_co[3], const float ray_dir[3], - const float ray_depth_range[2], const float scale[3], const bool is_persp, - /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, - /* return args */ - float r_co[3], float r_no[3]) -{ - const float v1_sc[3] = { - v1[0] * scale[0], - v1[1] * scale[1], - v1[2] * scale[2], - }; - const float v2_sc[3] = { - v2[0] * scale[0], - v2[1] * scale[1], - v2[2] * scale[2], - }; - const float co_sc[3] = { - ray_co[0] * scale[0], - ray_co[1] * scale[1], - ray_co[2] * scale[2], - }; - const float dir_sc[3] = { - ray_dir[0] * scale[0], - ray_dir[1] * scale[1], - ray_dir[2] * scale[2], - }; - - float tmp_co[3], depth; - float dist_sq = dist_squared_ray_to_seg_v3(co_sc, dir_sc, v1_sc, v2_sc, tmp_co, &depth); - - if (depth < ray_depth_range[0]) - return false; - - if (is_persp) - dist_sq /= SQUARE(depth); - - if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) { - *dist_to_ray_sq = dist_sq; - - tmp_co[0] /= scale[0]; - tmp_co[1] /= scale[1]; - tmp_co[2] /= scale[2]; - - copy_v3_v3(r_co, tmp_co); - - if (r_no) { - sub_v3_v3v3(r_no, v1, v2); - } - - *ray_depth = depth; - return true; - } - return false; -} - -static void test_vert_depth_cb( +static void test_vert_ray_dist_cb( void *userdata, const float origin[3], const float dir[3], const float scale[3], int index, BVHTreeNearest *nearest) { struct NearestDM_Data *ndata = userdata; - const BVHTreeFromMesh *data = ndata->bvhdata; - const MVert *vert = data->vert + index; + const struct BVHTreeFromMeshType *data = ndata->bvhdata; + + const float *co = get_vert_co(data, index); - if (test_vert( - vert->co, NULL, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp, - ndata->ray_depth, &nearest->dist_sq, - nearest->co, NULL)) + if (test_vert_dist( + co, origin, dir, ndata->depth_range, + scale, ndata->ray_depth, &nearest->dist_sq, + nearest->co)) { - normal_short_to_float_v3(nearest->no, vert->no); + copy_vert_no(data, index, nearest->no); nearest->index = index; } } -static void test_edge_depth_cb( +static void test_edge_ray_dist_cb( void *userdata, const float origin[3], const float dir[3], const float scale[3], int index, BVHTreeNearest *nearest) { struct NearestDM_Data *ndata = userdata; - const BVHTreeFromMesh *data = ndata->bvhdata; - const MVert *vert = data->vert; - const MEdge *edge = data->edge + index; - - if (test_edge( - vert[edge->v1].co, vert[edge->v2].co, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp, - ndata->ray_depth, &nearest->dist_sq, - nearest->co, nearest->no)) + BVHTreeFromMeshType *data = ndata->bvhdata; + + const float *v_pair[2]; + get_edge_verts(data, index, v_pair); + + if (test_edge_dist( + v_pair[0], v_pair[1], origin, dir, ndata->depth_range, + scale, ndata->ray_depth, &nearest->dist_sq, + nearest->co)) { + sub_v3_v3v3(nearest->no, v_pair[0], v_pair[1]); nearest->index = index; } } @@ -850,10 +1073,11 @@ static void test_edge_depth_cb( static bool snapDerivedMesh( SnapObjectContext *sctx, Object *ob, DerivedMesh *dm, float obmat[4][4], const unsigned int ob_index, - const short snap_to, bool do_bb, - const float ray_origin[3], const float ray_start[3], const float ray_normal[3], const float ray_depth_range[2], + const short snap_to, const float mval[2], const enum eViewProj view_proj, bool do_bb, + const float ray_origin[3], const float ray_start[3], const float ray_normal[3], + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, float *dist_px, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float r_no[3], int *r_index, ListBase *r_hit_list) @@ -865,7 +1089,7 @@ static bool snapDerivedMesh( return retval; } } - if (snap_to == SCE_SNAP_MODE_EDGE) { + else if (snap_to == SCE_SNAP_MODE_EDGE) { if (dm->getNumEdges(dm) == 0) { return retval; } @@ -877,8 +1101,7 @@ static bool snapDerivedMesh( } { - const bool is_persp = sctx->use_v3d && ((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp; - bool need_ray_start_correction_init = (snap_to == SCE_SNAP_MODE_FACE) && sctx->use_v3d && !is_persp; + bool need_ray_start_correction_init = (snap_to == SCE_SNAP_MODE_FACE) && (view_proj == VIEW_PROJ_ORTHO); float imat[4][4]; float timat[3][3]; /* transpose inverse matrix for normals */ @@ -985,175 +1208,161 @@ static bool snapDerivedMesh( break; } } - switch (snap_to) { - case SCE_SNAP_MODE_FACE: - { - /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already - * been *inside* boundbox, leading to snap failures (see T38409). - * Note also ar might be null (see T38435), in this case we assume ray_start is ok! - */ - if (sctx->use_v3d && !is_persp) { /* do_ray_start_correction */ - if (need_ray_start_correction_init) { - /* We *need* a reasonably valid len_diff in this case. - * Use BHVTree to find the closest face from ray_start_local. - */ - if (treedata && treedata->tree != NULL) { - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - /* Compute and store result. */ - BLI_bvhtree_find_nearest( - treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata); - if (nearest.index != -1) { - float dvec[3]; - sub_v3_v3v3(dvec, nearest.co, ray_start_local); - len_diff = dot_v3v3(dvec, ray_normal_local); - } - } - } - float ray_org_local[3]; - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); + if (!treedata || !treedata->tree) { + return retval; + } - /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with very far - * away ray_start values (as returned in case of ortho view3d), see T38358. + if (snap_to == SCE_SNAP_MODE_FACE) { + /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already + * been *inside* boundbox, leading to snap failures (see T38409). + * Note also ar might be null (see T38435), in this case we assume ray_start is ok! + */ + if (view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */ + if (need_ray_start_correction_init) { + /* We *need* a reasonably valid len_diff in this case. + * Use BHVTree to find the closest face from ray_start_local. */ - len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ - madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local, - len_diff + ray_depth_range[0]); - local_depth -= len_diff; - } - else { - len_diff = 0.0f; - } - if (r_hit_list) { - struct RayCastAll_Data data; - - data.bvhdata = treedata; - data.raycast_callback = treedata->raycast_callback; - data.obmat = obmat; - data.timat = timat; - data.len_diff = len_diff; - data.local_scale = local_scale; - data.ob = ob; - data.ob_uuid = ob_index; - data.dm = dm; - data.hit_list = r_hit_list; - data.retval = retval; - - BLI_bvhtree_ray_cast_all( - treedata->tree, ray_start_local, ray_normal_local, 0.0f, - *ray_depth, raycast_all_cb, &data); - - retval = data.retval; + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + /* Compute and store result. */ + BLI_bvhtree_find_nearest( + treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata); + if (nearest.index != -1) { + float dvec[3]; + sub_v3_v3v3(dvec, nearest.co, ray_start_local); + len_diff = dot_v3v3(dvec, ray_normal_local); + } } - else { - BVHTreeRayHit hit; - - hit.index = -1; - hit.dist = local_depth; - - if (treedata->tree && - BLI_bvhtree_ray_cast( - treedata->tree, ray_start_local, ray_normal_local, 0.0f, - &hit, treedata->raycast_callback, treedata) != -1) - { - hit.dist += len_diff; - hit.dist /= local_scale; - if (hit.dist <= *ray_depth) { - *ray_depth = hit.dist; - copy_v3_v3(r_loc, hit.co); - copy_v3_v3(r_no, hit.no); + float ray_org_local[3]; + + copy_v3_v3(ray_org_local, ray_origin); + mul_m4_v3(imat, ray_org_local); + + /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with very far + * away ray_start values (as returned in case of ortho view3d), see T38358. + */ + len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ + madd_v3_v3v3fl( + ray_start_local, ray_org_local, ray_normal_local, len_diff + depth_range[0] * local_scale); + local_depth -= len_diff; + } + else { + len_diff = 0.0f; + } + if (r_hit_list) { + struct RayCastAll_Data data; + + data.bvhdata = treedata; + data.raycast_callback = treedata->raycast_callback; + data.obmat = obmat; + data.timat = timat; + data.len_diff = len_diff; + data.local_scale = local_scale; + data.ob = ob; + data.ob_uuid = ob_index; + data.hit_list = r_hit_list; + data.retval = retval; + + BLI_bvhtree_ray_cast_all( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + *ray_depth, raycast_all_cb, &data); + + retval = data.retval; + } + else { + BVHTreeRayHit hit = {.index = -1, .dist = local_depth}; + + if (BLI_bvhtree_ray_cast( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + &hit, treedata->raycast_callback, treedata) != -1) + { + hit.dist += len_diff; + hit.dist /= local_scale; + if (hit.dist <= *ray_depth) { + *ray_depth = hit.dist; + copy_v3_v3(r_loc, hit.co); + + /* back to worldspace */ + mul_m4_v3(obmat, r_loc); - /* back to worldspace */ - mul_m4_v3(obmat, r_loc); + if (r_no) { + copy_v3_v3(r_no, hit.no); mul_m3_v3(timat, r_no); normalize_v3(r_no); + } - retval = true; + retval = true; - if (r_index) { - *r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]); - } + if (r_index) { + *r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]); } } } - break; } - case SCE_SNAP_MODE_VERTEX: - { - float ray_org_local[3]; + } + else { + const ARegion *ar = sctx->v3d_data.ar; - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); + float ray_org_local[3]; + copy_v3_v3(ray_org_local, ray_origin); + mul_m4_v3(imat, ray_org_local); - BVHTreeNearest nearest; + BVHTreeFromMeshType treedata_type = {.userdata = treedata, .type = SNAP_MESH}; - nearest.index = -1; - nearest.dist_sq = *dist_to_ray_sq; + if (view_proj == VIEW_PROJ_PERSP) { + Object_Nearest2dPrecalc neasrest_precalc; + neasrest_precalc.userdata = &treedata_type; + neasrest_precalc.index = -1; - struct NearestDM_Data userdata; - userdata.bvhdata = treedata; - userdata.is_persp = is_persp; - userdata.ray_depth_range = ray_depth_range; - userdata.ray_depth = ray_depth; + nearest2d_precalc(&neasrest_precalc, ar, *dist_px, obmat, + ray_org_local, ray_normal_local, mval, depth_range); - float ob_scale[3]; - mat4_to_size(ob_scale, obmat); + BVHTree_WalkLeafCallback cb_walk_leaf = + (snap_to == SCE_SNAP_MODE_VERTEX) ? + cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; - if (treedata->tree && ( - is_persp ? - BLI_bvhtree_find_nearest_to_ray_angle( - treedata->tree, ray_org_local, ray_normal_local, - true, ob_scale, &nearest, test_vert_depth_cb, &userdata) : - BLI_bvhtree_find_nearest_to_ray( - treedata->tree, ray_org_local, ray_normal_local, - true, ob_scale, &nearest, test_vert_depth_cb, &userdata)) != -1) - { - copy_v3_v3(r_loc, nearest.co); + BLI_bvhtree_walk_dfs( + treedata->tree, + cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest_precalc); + + if (neasrest_precalc.index != -1) { + copy_v3_v3(r_loc, neasrest_precalc.co); mul_m4_v3(obmat, r_loc); if (r_no) { - copy_v3_v3(r_no, nearest.no); + copy_v3_v3(r_no, neasrest_precalc.no); mul_m3_v3(timat, r_no); normalize_v3(r_no); } - *dist_px *= nearest.dist_sq / (*dist_to_ray_sq); - *dist_to_ray_sq = nearest.dist_sq; + *dist_px = sqrtf(neasrest_precalc.projectdefs.dist_px_sq); retval = true; } - break; } - case SCE_SNAP_MODE_EDGE: - { - float ray_org_local[3]; - - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); - + else { BVHTreeNearest nearest; nearest.index = -1; - nearest.dist_sq = *dist_to_ray_sq; + float dist_3d = dist_px_to_dist3d_or_tangent(ar, *dist_px); + nearest.dist_sq = SQUARE(dist_3d); - struct NearestDM_Data userdata; - userdata.bvhdata = treedata; - userdata.is_persp = is_persp; - userdata.ray_depth_range = ray_depth_range; - userdata.ray_depth = ray_depth; float ob_scale[3]; mat4_to_size(ob_scale, obmat); - if (treedata->tree && ( - is_persp ? - BLI_bvhtree_find_nearest_to_ray_angle( - treedata->tree, ray_org_local, ray_normal_local, - true, ob_scale, &nearest, test_edge_depth_cb, &userdata) : - BLI_bvhtree_find_nearest_to_ray( - treedata->tree, ray_org_local, ray_normal_local, - true, ob_scale, &nearest, test_edge_depth_cb, &userdata)) != -1) + struct NearestDM_Data userdata; + userdata.bvhdata = &treedata_type; + userdata.depth_range = depth_range; + userdata.ray_depth = ray_depth; + + BVHTree_NearestToRayCallback cb_test_ray_dist = + (snap_to == SCE_SNAP_MODE_VERTEX) ? + test_vert_ray_dist_cb : test_edge_ray_dist_cb; + + if (BLI_bvhtree_find_nearest_to_ray( + treedata->tree, ray_org_local, ray_normal_local, + true, ob_scale, &nearest, cb_test_ray_dist, &userdata) != -1) { copy_v3_v3(r_loc, nearest.co); mul_m4_v3(obmat, r_loc); @@ -1162,12 +1371,10 @@ static bool snapDerivedMesh( mul_m3_v3(timat, r_no); normalize_v3(r_no); } - *dist_px *= nearest.dist_sq / (*dist_to_ray_sq); - *dist_to_ray_sq = nearest.dist_sq; + *dist_px *= sqrtf(nearest.dist_sq) / dist_3d; retval = true; } - break; } } @@ -1181,47 +1388,14 @@ static bool snapDerivedMesh( return retval; } -static void test_bmvert_depth_cb( - void *userdata, const float origin[3], const float dir[3], - const float scale[3], int index, BVHTreeNearest *nearest) -{ - struct NearestDM_Data *ndata = userdata; - const BMEditMesh *em = ndata->bvhdata; - BMVert *eve = BM_vert_at_index(em->bm, index); - - if (test_vert( - eve->co, eve->no, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp, - ndata->ray_depth, &nearest->dist_sq, - nearest->co, nearest->no)) - { - nearest->index = index; - } -} - -static void test_bmedge_depth_cb( - void *userdata, const float origin[3], const float dir[3], - const float scale[3], int index, BVHTreeNearest *nearest) -{ - struct NearestDM_Data *ndata = userdata; - const BMEditMesh *em = ndata->bvhdata; - BMEdge *eed = BM_edge_at_index(em->bm, index); - - if (test_edge( - eed->v1->co, eed->v2->co, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp, - ndata->ray_depth, &nearest->dist_sq, - nearest->co, nearest->no)) - { - nearest->index = index; - } -} - static bool snapEditMesh( SnapObjectContext *sctx, Object *ob, BMEditMesh *em, float obmat[4][4], const unsigned int ob_index, - float *dist_px, const short snap_to, - const float ray_origin[3], const float ray_start[3], const float ray_normal[3], const float ray_depth_range[2], + const short snap_to, const float mval[2], const enum eViewProj view_proj, + const float ray_origin[3], const float ray_start[3], const float ray_normal[3], + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float r_no[3], int *r_index, ListBase *r_hit_list) @@ -1245,8 +1419,6 @@ static bool snapEditMesh( } { - const bool is_persp = (sctx->use_v3d && ((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp); - float imat[4][4]; float timat[3][3]; /* transpose inverse matrix for normals */ float ray_normal_local[3]; @@ -1351,183 +1523,168 @@ static bool snapEditMesh( } } - switch (snap_to) { - case SCE_SNAP_MODE_FACE: - { - float ray_start_local[3]; - copy_v3_v3(ray_start_local, ray_start); - mul_m4_v3(imat, ray_start_local); - - /* local scale in normal direction */ - float local_scale = normalize_v3(ray_normal_local); - float local_depth = *ray_depth; - if (local_depth != BVH_RAYCAST_DIST_MAX) { - local_depth *= local_scale; - } + if (!treedata || !treedata->tree) { + return retval; + } + + if (snap_to == SCE_SNAP_MODE_FACE) { + float ray_start_local[3]; + copy_v3_v3(ray_start_local, ray_start); + mul_m4_v3(imat, ray_start_local); + + /* local scale in normal direction */ + float local_scale = normalize_v3(ray_normal_local); + float local_depth = *ray_depth; + if (local_depth != BVH_RAYCAST_DIST_MAX) { + local_depth *= local_scale; + } - /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already - * been *inside* boundbox, leading to snap failures (see T38409). - * Note also ar might be null (see T38435), in this case we assume ray_start is ok! + /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already + * been *inside* boundbox, leading to snap failures (see T38409). + * Note also ar might be null (see T38435), in this case we assume ray_start is ok! + */ + float len_diff = 0.0f; + if (view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */ + /* We *need* a reasonably valid len_diff in this case. + * Use BHVTree to find the closest face from ray_start_local. */ - float len_diff = 0.0f; - if (sctx->use_v3d && !is_persp) { /* do_ray_start_correction */ - /* We *need* a reasonably valid len_diff in this case. - * Use BHVTree to find the closest face from ray_start_local. + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + /* Compute and store result. */ + if (BLI_bvhtree_find_nearest( + treedata->tree, ray_start_local, &nearest, NULL, NULL) != -1) + { + float dvec[3]; + sub_v3_v3v3(dvec, nearest.co, ray_start_local); + len_diff = dot_v3v3(dvec, ray_normal_local); + float ray_org_local[3]; + + copy_v3_v3(ray_org_local, ray_origin); + mul_m4_v3(imat, ray_org_local); + + /* We pass a temp ray_start, set from object's boundbox, + * to avoid precision issues with very far away ray_start values + * (as returned in case of ortho view3d), see T38358. */ - if (treedata && treedata->tree != NULL) { - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - /* Compute and store result. */ - if (BLI_bvhtree_find_nearest( - treedata->tree, ray_start_local, &nearest, NULL, NULL) != -1) - { - float dvec[3]; - sub_v3_v3v3(dvec, nearest.co, ray_start_local); - len_diff = dot_v3v3(dvec, ray_normal_local); - float ray_org_local[3]; - - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); - - /* We pass a temp ray_start, set from object's boundbox, - * to avoid precision issues with very far away ray_start values - * (as returned in case of ortho view3d), see T38358. - */ - len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ - madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local, - len_diff + ray_depth_range[0]); - local_depth -= len_diff; - } - } - } - if (r_hit_list) { - struct RayCastAll_Data data; - - data.bvhdata = treedata; - data.raycast_callback = treedata->raycast_callback; - data.obmat = obmat; - data.timat = timat; - data.len_diff = len_diff; - data.local_scale = local_scale; - data.ob = ob; - data.ob_uuid = ob_index; - data.dm = NULL; - data.hit_list = r_hit_list; - data.retval = retval; - - BLI_bvhtree_ray_cast_all( - treedata->tree, ray_start_local, ray_normal_local, 0.0f, - *ray_depth, raycast_all_cb, &data); - - retval = data.retval; + len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ + madd_v3_v3v3fl( + ray_start_local, ray_org_local, ray_normal_local, len_diff + depth_range[0] * local_scale); + local_depth -= len_diff; } - else { - BVHTreeRayHit hit; - - hit.index = -1; - hit.dist = local_depth; - - if (treedata->tree && - BLI_bvhtree_ray_cast( - treedata->tree, ray_start_local, ray_normal_local, 0.0f, - &hit, treedata->raycast_callback, treedata) != -1) - { - hit.dist += len_diff; - hit.dist /= local_scale; - if (hit.dist <= *ray_depth) { - *ray_depth = hit.dist; - copy_v3_v3(r_loc, hit.co); - copy_v3_v3(r_no, hit.no); + } + if (r_hit_list) { + struct RayCastAll_Data data; + + data.bvhdata = treedata; + data.raycast_callback = treedata->raycast_callback; + data.obmat = obmat; + data.timat = timat; + data.len_diff = len_diff; + data.local_scale = local_scale; + data.ob = ob; + data.ob_uuid = ob_index; + data.hit_list = r_hit_list; + data.retval = retval; + + BLI_bvhtree_ray_cast_all( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + *ray_depth, raycast_all_cb, &data); + + retval = data.retval; + } + else { + BVHTreeRayHit hit = {.index = -1, .dist = local_depth}; - /* back to worldspace */ - mul_m4_v3(obmat, r_loc); + if (BLI_bvhtree_ray_cast( + treedata->tree, ray_start_local, ray_normal_local, 0.0f, + &hit, treedata->raycast_callback, treedata) != -1) + { + hit.dist += len_diff; + hit.dist /= local_scale; + if (hit.dist <= *ray_depth) { + *ray_depth = hit.dist; + copy_v3_v3(r_loc, hit.co); + + /* back to worldspace */ + mul_m4_v3(obmat, r_loc); + + if (r_no) { + copy_v3_v3(r_no, hit.no); mul_m3_v3(timat, r_no); normalize_v3(r_no); + } - retval = true; + retval = true; - if (r_index) { - *r_index = hit.index; - } + if (r_index) { + *r_index = hit.index; } } } - break; } - case SCE_SNAP_MODE_EDGE: - { - float ray_org_local[3]; + } + else { + const ARegion *ar = sctx->v3d_data.ar; - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); + float ray_org_local[3]; + copy_v3_v3(ray_org_local, ray_origin); + mul_m4_v3(imat, ray_org_local); - BVHTreeNearest nearest; + BVHTreeFromMeshType treedata_type = {.userdata = treedata, .type = SNAP_EDIT_MESH}; - nearest.index = -1; - nearest.dist_sq = *dist_to_ray_sq; + if (view_proj == VIEW_PROJ_PERSP) { + Object_Nearest2dPrecalc neasrest_precalc; + neasrest_precalc.userdata = &treedata_type; + neasrest_precalc.index = -1; - struct NearestDM_Data userdata; - userdata.bvhdata = em; - userdata.is_persp = is_persp; - userdata.ray_depth_range = ray_depth_range; - userdata.ray_depth = ray_depth; + nearest2d_precalc(&neasrest_precalc, ar, *dist_px, obmat, + ray_org_local, ray_normal_local, mval, depth_range); - float ob_scale[3]; - mat4_to_size(ob_scale, obmat); + BVHTree_WalkLeafCallback cb_walk_leaf = + (snap_to == SCE_SNAP_MODE_VERTEX) ? + cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; - if (treedata->tree && ( - is_persp ? - BLI_bvhtree_find_nearest_to_ray_angle( - treedata->tree, ray_org_local, ray_normal_local, - false, ob_scale, &nearest, test_bmedge_depth_cb, &userdata) : - BLI_bvhtree_find_nearest_to_ray( - treedata->tree, ray_org_local, ray_normal_local, - false, ob_scale, &nearest, test_bmedge_depth_cb, &userdata)) != -1) - { - copy_v3_v3(r_loc, nearest.co); + BLI_bvhtree_walk_dfs( + treedata->tree, + cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest_precalc); + + if (neasrest_precalc.index != -1) { + copy_v3_v3(r_loc, neasrest_precalc.co); mul_m4_v3(obmat, r_loc); if (r_no) { - copy_v3_v3(r_no, nearest.no); + copy_v3_v3(r_no, neasrest_precalc.no); mul_m3_v3(timat, r_no); normalize_v3(r_no); } - *dist_px *= nearest.dist_sq / (*dist_to_ray_sq); - *dist_to_ray_sq = nearest.dist_sq; + *dist_px = sqrtf(neasrest_precalc.projectdefs.dist_px_sq); retval = true; } - break; } - case SCE_SNAP_MODE_VERTEX: - { - float ray_org_local[3]; - - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); - + else { BVHTreeNearest nearest; nearest.index = -1; - nearest.dist_sq = *dist_to_ray_sq; + float dist_3d = dist_px_to_dist3d_or_tangent(ar, *dist_px); + nearest.dist_sq = SQUARE(dist_3d); - struct NearestDM_Data userdata; - userdata.bvhdata = em; - userdata.is_persp = is_persp; - userdata.ray_depth_range = ray_depth_range; - userdata.ray_depth = ray_depth; float ob_scale[3]; mat4_to_size(ob_scale, obmat); - if (treedata->tree && ( - is_persp ? - BLI_bvhtree_find_nearest_to_ray_angle( - treedata->tree, ray_org_local, ray_normal_local, - false, ob_scale, &nearest, test_bmvert_depth_cb, &userdata) : - BLI_bvhtree_find_nearest_to_ray( - treedata->tree, ray_org_local, ray_normal_local, - false, ob_scale, &nearest, test_bmedge_depth_cb, &userdata)) != -1) + struct NearestDM_Data userdata; + userdata.bvhdata = &treedata_type; + userdata.depth_range = depth_range; + userdata.ray_depth = ray_depth; + + BVHTree_NearestToRayCallback cb_test_ray_dist = + (snap_to == SCE_SNAP_MODE_VERTEX) ? + test_vert_ray_dist_cb : test_edge_ray_dist_cb; + + if (BLI_bvhtree_find_nearest_to_ray( + treedata->tree, ray_org_local, ray_normal_local, + false, ob_scale, &nearest, cb_test_ray_dist, &userdata) != -1) { copy_v3_v3(r_loc, nearest.co); mul_m4_v3(obmat, r_loc); @@ -1536,12 +1693,10 @@ static bool snapEditMesh( mul_m3_v3(timat, r_no); normalize_v3(r_no); } - *dist_px *= nearest.dist_sq / (*dist_to_ray_sq); - *dist_to_ray_sq = nearest.dist_sq; + *dist_px *= sqrtf(nearest.dist_sq) / dist_3d; retval = true; } - break; } } @@ -1557,10 +1712,6 @@ static bool snapEditMesh( /** * \param use_obedit: Uses the coordinates of BMesh (if any) to do the snapping; - * \param ray_depth_range: - * - 0: distance from the ray_origin to the clipping plane min (can be negative). - * - 1: maximum distance, elements outside this are ignored. - * \param ray_depth: maximum depth allowed for r_co. * * \note Duplicate args here are documented at #snapObjectsRay */ @@ -1568,15 +1719,21 @@ static bool snapObject( SnapObjectContext *sctx, Object *ob, float obmat[4][4], const unsigned int ob_index, bool use_obedit, const short snap_to, const float mval[2], - const float ray_origin[3], const float ray_start[3], const float ray_normal[3], const float ray_depth_range[2], + const float ray_origin[3], const float ray_start[3], const float ray_normal[3], + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, float *dist_px, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4], ListBase *r_hit_list) { + const enum eViewProj view_proj = + ((sctx->use_v3d == false) || (mval == NULL)) ? VIEW_PROJ_NONE : + (((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO); + const ARegion *ar = sctx->v3d_data.ar; + bool retval = false; if (ob->type == OB_MESH) { @@ -1586,9 +1743,9 @@ static bool snapObject( em = BKE_editmesh_from_object(ob); retval = snapEditMesh( sctx, ob, em, obmat, ob_index, - dist_px, snap_to, - ray_origin, ray_start, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, + snap_to, mval, view_proj, + ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, r_hit_list); } @@ -1605,42 +1762,43 @@ static bool snapObject( } retval = snapDerivedMesh( sctx, ob, dm, obmat, ob_index, - snap_to, true, - ray_origin, ray_start, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, dist_px, + snap_to, mval, view_proj, true, + ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, r_hit_list); dm->release(dm); } } - else if (ob->type == OB_ARMATURE) { - retval = snapArmature( - ar, ob, ob->data, obmat, mval, snap_to, - ray_start, ray_normal, - ray_depth, dist_px, - r_loc, r_no); - } - else if (ob->type == OB_CURVE) { - retval = snapCurve( - ar, ob, ob->data, obmat, mval, snap_to, - ray_start, ray_normal, - ray_depth, dist_px, - r_loc, r_no); - } - else if (ob->type == OB_EMPTY) { - retval = snapEmpty( - ar, ob, obmat, mval, snap_to, - ray_start, ray_normal, - ray_depth, dist_px, - r_loc, r_no); - } - else if (ob->type == OB_CAMERA) { - retval = snapCamera( - ar, sctx->scene, ob, obmat, mval, snap_to, - ray_start, ray_normal, - ray_depth, dist_px, - r_loc, r_no); + else if (snap_to != SCE_SNAP_MODE_FACE) { + if (ob->type == OB_ARMATURE) { + retval = snapArmature( + ar, ob, ob->data, obmat, snap_to, ray_origin, ray_normal, + mval, view_proj, depth_range, dist_px, + r_loc, r_no); + } + else if (ob->type == OB_CURVE) { + retval = snapCurve( + ar, ob, ob->data, obmat, snap_to, mval, view_proj, + depth_range, + dist_px, + r_loc, r_no); + } + else if (ob->type == OB_EMPTY) { + retval = snapEmpty( + ar, ob, obmat, snap_to, mval, view_proj, + depth_range, + dist_px, + r_loc, r_no); + } + else if (ob->type == OB_CAMERA) { + retval = snapCamera( + sctx, ob, obmat, snap_to, mval, view_proj, + depth_range, + dist_px, + r_loc, r_no); + } } if (retval) { @@ -1666,20 +1824,18 @@ static bool snapObject( * \param snap_select: from enum SnapSelect. * * \param use_object_edit_cage: Uses the coordinates of BMesh (if any) to do the snapping. - * \param mval: Optional screen-space 2D location we're snapping to (may phase out). + * \param mval: Mouse coords. + * When NULL, ray-casting is handled without any projection matrix correction. * \param ray_origin: ray_start before being moved toward the ray_normal at the distance from vew3d clip_min. * \param ray_start: ray_origin moved for the start clipping plane (clip_min). * \param ray_normal: Unit length direction of the ray. + * \param depth_range: distances of clipe plane min and clip plane max; * * Read/Write Args * --------------- * * \param ray_depth: maximum depth allowed for r_co, elements deeper than this value will be ignored. - * \param dist_to_ray_sq: Real distance (3D) or Tangent (view cone radius at distance 1.0) squared. - * resulting of the function #dist_px_to_dist3d_or_tangent. - * - * \param dist_px: Pixel distance to element, - * note that this will eventually be replaced entirely by \a dist_to_ray_sq. + * \param dist_px: Maximum threshold distance (in pixels). * * Output Args * ----------- @@ -1698,8 +1854,9 @@ static bool snapObjectsRay( const unsigned short snap_to, const SnapSelect snap_select, const bool use_object_edit_cage, const float mval[2], const float ray_origin[3], const float ray_start[3], const float ray_normal[3], + const float depth_range[2], /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, float *dist_px, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4], @@ -1707,14 +1864,6 @@ static bool snapObjectsRay( { bool retval = false; - float dvec[3]; - sub_v3_v3v3(dvec, ray_start, ray_origin); - - const float ray_depth_range[2] = { - dot_v3v3(dvec, ray_normal), - *ray_depth, - }; - unsigned int ob_index = 0; Object *obedit = use_object_edit_cage ? sctx->scene->obedit : NULL; @@ -1730,8 +1879,8 @@ static bool snapObjectsRay( retval |= snapObject( sctx, ob, ob->obmat, ob_index++, false, snap_to, mval, - ray_origin, ray_start, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, dist_px, + ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); } @@ -1766,8 +1915,8 @@ static bool snapObjectsRay( retval |= snapObject( sctx, dupli_snap, dupli_ob->mat, ob_index++, use_obedit_dupli, snap_to, mval, - ray_origin, ray_start, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, dist_px, + ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); } @@ -1780,8 +1929,8 @@ static bool snapObjectsRay( retval |= snapObject( sctx, ob_snap, ob->obmat, ob_index++, use_obedit, snap_to, mval, - ray_origin, ray_start, ray_normal, ray_depth_range, - ray_depth, dist_to_ray_sq, dist_px, + ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); } } @@ -1888,14 +2037,12 @@ bool ED_transform_snap_object_project_ray_ex( float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4]) { - float dist_to_ray_sq = 0.0f; - + const float depth_range[2] = {0.0f, FLT_MAX}; return snapObjectsRay( sctx, - snap_to, params->snap_select, params->use_object_edit_cage, - NULL, - ray_start, ray_start, ray_normal, - ray_depth, &dist_to_ray_sq, NULL, + snap_to, params->snap_select, params->use_object_edit_cage, NULL, + ray_start, ray_start, ray_normal, depth_range, + ray_depth, NULL, r_loc, r_no, r_index, r_ob, r_obmat, NULL); } @@ -1914,8 +2061,7 @@ bool ED_transform_snap_object_project_ray_all( float ray_depth, bool sort, ListBase *r_hit_list) { - float dist_to_ray_sq = 0.0f; - + const float depth_range[2] = {0.0f, FLT_MAX}; if (ray_depth == -1.0f) { ray_depth = BVH_RAYCAST_DIST_MAX; } @@ -1927,8 +2073,8 @@ bool ED_transform_snap_object_project_ray_all( bool retval = snapObjectsRay( sctx, snap_to, params->snap_select, params->use_object_edit_cage, NULL, - ray_start, ray_start, ray_normal, - &ray_depth, &dist_to_ray_sq, NULL, + ray_start, ray_start, ray_normal, depth_range, + &ray_depth, NULL, NULL, NULL, NULL, NULL, NULL, r_hit_list); @@ -1983,11 +2129,6 @@ bool ED_transform_snap_object_project_ray( ray_depth = &ray_depth_fallback; } - float no_fallback[3]; - if (r_no == NULL) { - r_no = no_fallback; - } - return transform_snap_context_project_ray_impl( sctx, params, @@ -2006,11 +2147,6 @@ static bool transform_snap_context_project_view3d_mixed_impl( float ray_depth = BVH_RAYCAST_DIST_MAX; bool is_hit = false; - float r_no_dummy[3]; - if (r_no == NULL) { - r_no = r_no_dummy; - } - const int elem_type[3] = {SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE, SCE_SNAP_MODE_FACE}; BLI_assert(snap_to_flag != 0); @@ -2037,21 +2173,6 @@ static bool transform_snap_context_project_view3d_mixed_impl( } /** - * From a threshold (maximum distance to snap in pixels) returns: - * - * - The *real* distance (3D) if you are in orthographic-view. - * - The *tangent* (view cone radius at distance 1.0) if you are in perspective-view. - */ -static float dist_px_to_dist3d_or_tangent(const ARegion *ar, const float dist_px) -{ - const RegionView3D *rv3d = ar->regiondata; - if (ar->winx >= ar->winy) - return 2 * (dist_px / ar->winx) / rv3d->winmat[0][0]; - else - return 2 * (dist_px / ar->winy) / rv3d->winmat[1][1]; -} - -/** * Convenience function for performing snapping. * * Given a 2D region value, snap to vert/edge/face. @@ -2087,47 +2208,36 @@ bool ED_transform_snap_object_project_view3d_ex( float *ray_depth, float r_loc[3], float r_no[3], int *r_index) { - float ray_start[3], ray_normal[3], ray_origin[3]; + float ray_origin[3], ray_start[3], ray_normal[3], depth_range[2], ray_end[3]; - float ray_depth_fallback; - if (ray_depth == NULL) { - ray_depth_fallback = BVH_RAYCAST_DIST_MAX; - ray_depth = &ray_depth_fallback; - } + const ARegion *ar = sctx->v3d_data.ar; + const RegionView3D *rv3d = ar->regiondata; - if (!ED_view3d_win_to_ray_ex( - sctx->v3d_data.ar, sctx->v3d_data.v3d, - mval, ray_origin, ray_normal, ray_start, true)) - { + ED_view3d_win_to_origin(ar, mval, ray_origin); + ED_view3d_win_to_vector(ar, mval, ray_normal); + + ED_view3d_clip_range_get( + sctx->v3d_data.v3d, sctx->v3d_data.ar->regiondata, + &depth_range[0], &depth_range[1], false); + + madd_v3_v3v3fl(ray_start, ray_origin, ray_normal, depth_range[0]); + madd_v3_v3v3fl(ray_end, ray_origin, ray_normal, depth_range[1]); + + if (!ED_view3d_clip_segment(rv3d, ray_start, ray_end)) { return false; } - float dist_to_ray_sq; - { - float radius = dist_px_to_dist3d_or_tangent(sctx->v3d_data.ar, *dist_px); - /** - * Workaround to use of cone (Instead of project the radius on view plane): - * In perspective view, the radius of the cone may decrease depending on the ray direction. - * This is more evident with small values of the `Viewport lens angle`. - * The threshold becomes distorted that way. - */ - RegionView3D *rv3d = sctx->v3d_data.ar->regiondata; - if (rv3d->is_persp) { - float view_dir[3]; - negate_v3_v3(view_dir, rv3d->viewinv[2]); - normalize_v3(view_dir); - radius *= dot_v3v3(ray_normal, view_dir); - } - - dist_to_ray_sq = SQUARE(radius); + float ray_depth_fallback; + if (ray_depth == NULL) { + ray_depth_fallback = BVH_RAYCAST_DIST_MAX; + ray_depth = &ray_depth_fallback; } return snapObjectsRay( sctx, snap_to, params->snap_select, params->use_object_edit_cage, - mval, - ray_origin, ray_start, ray_normal, - ray_depth, &dist_to_ray_sq, dist_px, + mval, ray_origin, ray_start, ray_normal, depth_range, + ray_depth, dist_px, r_loc, r_no, r_index, NULL, NULL, NULL); } diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index e2f60955c81..1f1a778cac7 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -105,7 +105,7 @@ void ED_editors_init(bContext *C) ob->mode = OB_MODE_OBJECT; data = ob->data; - if (ob == obact && !ob->id.lib && !(data && data->lib)) + if (ob == obact && !ID_IS_LINKED_DATABLOCK(ob) && !(data && ID_IS_LINKED_DATABLOCK(data))) ED_object_toggle_modes(C, mode); } } diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index b3190ef784f..bdfff123aa4 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -743,7 +743,7 @@ static PVert *p_vert_add(PHandle *handle, PHashKey key, const float co[3], PEdge /* Sanity check, a single nan/inf point causes the entire result to be invalid. * Note that values within the calculation may _become_ non-finite, - * so the rest of the code still needs to take this possability into account. */ + * so the rest of the code still needs to take this possibility into account. */ for (int i = 0; i < 3; i++) { if (UNLIKELY(!isfinite(v->co[i]))) { v->co[i] = 0.0f; @@ -2804,7 +2804,7 @@ static PBool p_chart_abf_solve(PChart *chart) static void p_chart_pin_positions(PChart *chart, PVert **pin1, PVert **pin2) { - if (pin1 == pin2) { + if (!*pin1 || !*pin2 || *pin1 == *pin2) { /* degenerate case */ PFace *f = chart->faces; *pin1 = f->edge->vert; @@ -3051,7 +3051,7 @@ static void p_chart_lscm_begin(PChart *chart, PBool live, PBool abf) p_chart_boundaries(chart, NULL, &outer); /* outer can be NULL with non-finite coords. */ - if (outer && !p_chart_symmetry_pins(chart, outer, &pin1, &pin2)) { + if (!(outer && p_chart_symmetry_pins(chart, outer, &pin1, &pin2))) { p_chart_extrema_verts(chart, &pin1, &pin2); } |